Hot reload with React.js

After building a pretty big project with Meteor and React, I feel like this is a kickass combination that really deserve a lot of attention. However, there were a bunch of things that deeply bothered me:

  • REALLY slow rebuild everytime I change something in any React component (I do SSR so maybe it was hurting while developing)
  • Every React component were global variables (really?.. no comment)
  • I was losing the state of my component on reload
  • Everytime I wanted an NPM library I needed to wrap it with browserify to work both on client and server

I really feel like we should get a better control over the Meteor build. One little tool that can fix this is webpack:

  • It has a really fast hot module reload
  • It allows you to bundle modules using import/require so no more global variables
  • You can bundle it with any NPM module
  • You don’t loose your state
  • You can get an awesome red box with the error that happened inside your component and as soon as you fix it the page work again with no refresh (most of the time)

I know somebody else made a project with webpack and hot-reload but I felt like it was a lot hackish. I took the opportunity of the new hot loader Dan Abramov did for Webpack (react-transform-webpack-hmr) and thew a new package: webpack:hot-reload.

If you would like to try it, go at https://github.com/thereactivestack/meteor-webpack-react-kickstart and all you need is clone the git to get started. You will be surprised how simple the boilerplate code is.

Let me know what you guys think of it. I feel like it should be more integrated within Meteor but I can go as far as it allows me to. However, I think it is key to keep an absolute control over the Webpack config. How do you guys think Meteor should handle this?

12 Likes

It seems pretty interesting. Need to take a deep look.

I’ve just cloned your repo and seems like prod-mode is broken.

When I run ./scripts/run-dev.sh I see “App!” on localhost:3000. When I run ./scripts/run-prod.sh I see nothing.

Please, provide more information about how everything works.

It’s not clear for me where I should describe Mongo Collections. I tried to describe them inside react folder, then I tried to insert to collection inside component but I had got message insert failed: Method not found in browser console. I don’t understand file load order. Also I see that my code is transformed (via webpack probably), so it would be hard to debug application. I don’t understand how sever rendering works: does it prefetches data from database or not?

This should be fixed. It was assuming you had webpack installed globally but it doesn’t need to.

Your Mongo collections should be defined inside the meteor folder, not the react folder. It will be available in React because they are global variables. I personally use the Collection global variable to store them all.

There is no file order, only modules. You can access any other module by importing it. The main entry point is the react/index.js file.

webpack is actually taking care of that. You will keep a clean call stack / file names. The line numbers are most likely wrong though but it is a general problem with JSX.

You also get a really nice red block with all the details of the error including the call stack with the correct file names and lines.

This depends on what you use. If you are using react-router-ssr, you can use the ReactMeteorData mixin to subscribe to Meteor collections and the data will be prefetched automatically. You might want to check the react-render-ssr readme instead.

Let me know if you think something can be done in a better way :smile:

Thank you for your reply! It becomes more clear on how to deal with kickstart. I solved the problem with run-prod: I just had to run ./scripts/build.sh first.

I’m currently playing with this kickstart project and it feels really awesome.

I have some more questions:

  1. Is it possible to put all scripts declaration to the end of <body>? Like Arunoda’s Flow Router does https://github.com/kadirahq/flow-router/tree/ssr with it’s setDeferScriptLoading?

  2. I would like to keep react components and styles for them in one place. Is it possible to describe styles inside react folder? Should I modify webpack config to achieve this?

Thanks for the feedback I’ll make sure to clarify a few points by making a more complete example.

It would be easy to fix in react-router-ssr. I think it should be the default behavior I will take a look.
EDIT: This is now the default behavior of react-router-ssr

A lot of people (like Instagram) likes to require CSS within their React component. By setting up the css-loader on webpack, it will bundle the CSS within the javascript and automatically inline the CSS in your page when you mount the component.

Here is the line: { test: /\.css$/, loader: "style-loader!css-loader" } and make sure to install style-loader and css-loader with NPM.

And then:

class Header extends Component {
  componentWillMount() {
    require('./Header.css');
  }

  render() {
    return <header>HEADER!</header>;
  }
}

However, if you have server-rendering, you will have a FOUC (flashed of unstyled content) because the CSS is in javascript. The way to fix that is to have a special loader only on the server that gather all the CSS you need for the first render and send it inside the HTML. Nothing support this ATM with Meteor.

Personally for now, my CSS is inside a client folder in the meteor folder.

I’ve just tried style-loader, it’s interesting. I didn’t know that instagram uses “css inside js” in production.

And what about such an approach: tell webpack to watch all .css inside react folder and copy them to e.g. meteor/styles folder on every change?

I’m very new to webpack, never used this before, so I’m not even sure if this is something what webpack can do.

I think there is a way to use the file-loader to copy .css files within the meteor folder. I’m not sure if it will work in development though because the files are not written on the disk, they are in memory using the webpack-dev-middleware.

I tried meteor-webpack-react. It does not feel as good as @benoitt’s kickstart.

  1. kickstart’s project structure is more way clear and understanable
  2. meteor-webpack-react continues to work even if I have an error inside .jsx. It just continues to work with previously working version of the component. The only way to see an error is a browser console.
    upd: seems like error handling was implemented recently

Both projects are good. I like webpack approach with all it’s goodies. For now I just prefer kickstart because of it’s simplicity.

1 Like

Examples…everybody needs examples ((-:

Their is only a kickstart project for now and i would like to keep it minimalist. I’m planning to write examples (todo app and/or slack clone).

I’m using it now with a pretty massive project and it works like a charm :slight_smile:

@benoitt, have you heard of Radium?

I would like to use Radium with kickstart, but I can’t figure out how.

What I do:

  1. Add dependency to package.json: "radium": "0.13.8"
  2. Run npm install
  3. Add import Radium from 'radium'; to my react/App.jsx file
  4. I see errors in the console:
ERROR in Loader /Users/imkost/Projects/meteor-webpack-react-
kickstart/node_modules/radium/node_modules/babel/index.js?
{"stage":0,"plugins":["react-transform"],"extra":{"react-transform":
[{"target":"react-transform-webpack-hmr","imports":["react"],
"locals":["module"]}, {"target":"react-transform-catch-errors",
"imports":["react","redbox-react"]}]}}
didn't return a function

Yes I’ve seen Radium before but never tried. It seems pretty cool.

I believe my code was missing exclude: /(node_modules|bower_components)/, on the babel-loader. If you add it, does it fix the problem?

Yes! Now it works! Thank you

this is awesome, i have a question, what is the most preferred way to deploy a webpack meteor app? can you still use MUP?

Nothing is different than Meteor except you need to make sure webpack is building the script first. When you call scripts/build.sh, all it does is it build your .js file with Webpack, save it inside your meteor application and call metor build or you can change it to meteor publish. You can certainly edit the build script to use MUP.

1 Like

Thanks, another question, is there any advantage in using meteor package as externals in webpack vs using npm packages? LIke react router for example. And how does scss mix with all of this?

edit: Hey i tied to use css-loader it works, but if i use sass-loader with this. And it gives the error:

 Error: `libsass` bindings not found in /desktop/meteor-webpack-react-kickstart/node_modules/node-sass/vendor/darwin-x64-11/binding.node. Try reinstalling `node-sass`?

This seems like a meteor-webpack-react-kickstart issue because running the same set up on a different express.js app works. And i tried to run it on a different machine it also gives the same error. I dont know if it is something i am not understanding with sass-loader and meteor working together.

I would rather use the npm version if it is the official one. However, react router is an exception because their is a mereor package with server rendering.

For SAAS and CSS loader, you need to be careful because the same code is used for server and client at the same time. You might need to change the webpack build to generate a server bundle with a null loader for the css.

I’m not sure why it doedn’t work on dev though. I personally use less inside a client folder inside the Meteor folder.

I’ve also started experimenting inlining styles inside my components using radium (thx imkost it’s awesome) or jsxstyle.

1 Like