The Death of React Hot Loader
Update
The ideas from this post have now materialized in babel-plugin-react-transform and its ecosystem. Check out react-transform-boilerplate!
React Hot Loader is my first JavaScript open-source project. It has enjoyed a tremendous response, and it has changed my professional life. True functional programmers don’t take it seriously, but comparing the wonder I felt the first time it worked to the mundane reality of hot reloading today, I know that it has made a difference in people’s minds, workflows and expectations.
New projects sharing the same vision, such as Amok by Casper Beyer and LiveReactload by Matti Lankinen, have appeared. Benoît Zugmeyer uses the core of React Hot Loader inside an atom-shell application. Just a few days ago Michael Johnston of the react-canvas fame managed to get React Hot Loader to work with React Native using react-native-webpack-server. I never expected any of this to happen, and I am thrilled and humbled to be in this company. Hell, I’m even going to ReactEurope!
The enthusiasm and the support I have received have been overwhelming. The flip side of this is that I’m always being painfully aware that React Hot Loader is a great hack. It’s not how hot reloading should work. What it shows is that it can work given today’s JavaScript applications’ constraints, despite the mutability, despite the state, and despite a myriad of other sins we commit in our apps.
In the long term, we should bring the better hot reloading solutions to the mainstream by spreading ideas, raising money for such projects, building tools, and documenting great, but obscure technologies that deserve to be widely adopted. I’ll be thrilled to deprecate React Hot Loader when I see a better way of achieving the same developer experience in JavaScript.
However, in the short term, there’s still a few things I want to fix in React Hot Loader before sending it off to gather dust in the legacy treasury of in-house IT departments.
Here’s how I think React Hot Loader 2 should be different.
- Instead of the weird module.makeHot opt-in API, export a decorator. Marking a class for hot reloading should be as easy as putting @hotify on it. In production, it would compile to a no-op, so there is no need for conditional statements in the user code. The project exporting @hotify decorator should be independent of Webpack. (Update: its alpha version is now available!) Re-evaluating the entire script with the new code with @hotify decorators in both scripts should have the same effect as hot reloading, except that such re-evaluation would usually cause unwanted side effects. (Which is where Webpack’s Hot Module Replacement fits nicely, providing granular bubbling module updates and module cache for the unchanged modules.)
- Hotifying only module exports doesn’t cut it anymore. It doesn’t work with higher-order components that wrap the real classes. Instead, we need to go back to finding React components in the source code. It was tricky and unreliable when React Hot Loader operated on the source file string, but what if it was a Babel plugin instead? (Update: its alpha version is now available too!) For example, a plugin could slap @hotify onto any class with render method. It could also be configured to process module exports and even locate components using a custom project-wide heuristic. That Babel plugin could even generate hot reloading code for Webpack (and potentially, other build systems). This way we may be able to converge with LiveReactload at some point.
- What about the CoffeeScript and other compile-to-JS users? Nothing prevents them from keeping using the old Webpack loader! It would just need to be changed to hotify exports using the same @hotify decorator that the Babel plugin would use.
We already have a separate “hot loader core” project, but its API is not usable directly and, frankly, hard to understand. In the coming months, I will work on making it more developer-friendly.
As the time passes, React Hot Loader will gradually disappear. Instead, I hope that an ecosystem will appear, each part ready to be replaced by something better when the time is right.
⚛