1 | React Hot API
|
2 | =========
|
3 |
|
4 | This is a generic library implementing hot reload for React components without unmounting or losing their state.
|
5 |
|
6 | **It is intended for build tool authors or adventurous folk and *not* for website development. For a reference implementation *that you can actually use*, check out [react-hot-loader](https://github.com/gaearon/react-hot-loader) for Webpack.**
|
7 |
|
8 | This library drives React hot-reload magic of **[react-hot-loader](https://github.com/gaearon/react-hot-loader)** but is not tied to Webpack itself, so alternative build systems that support hot-reloading individual modules can use it to implement **[live-editing for React components](http://gaearon.github.io/react-hot-loader/)**.
|
9 |
|
10 | **[See the video.](https://vimeo.com/100010922)**
|
11 |
|
12 | ### API
|
13 |
|
14 | #### `makeHot: (ReactClass, persistentId?) => ReactClass`
|
15 |
|
16 | Registers a hot-reloadable React class. If you don't pass `persistentId`, it is inferred from `ReactClass.displayName` or `ReactClass.name` (for ES6 classes). When called for the first time, it will merely return the passed class. When called the next time with the same `persistentId`, will patch original class with the prototype of the new class, and return the original class.
|
17 |
|
18 | #### `require('react-hot-api'): (getRootInstances) => makeHot`
|
19 |
|
20 | Invoke this once within each hot-reloadable module to obtain the function described above.
|
21 | You must pass the result between *all emitted versions of the same module* for hot reload to work.
|
22 |
|
23 | `getRootInstances` is a method you as a caller should provide. It should return all root components on the page.
|
24 | You can implement it by returning `require('react/lib/ReactMount')._instancesByReactRootID` but you may also want to return some known root instance, for example, if you host React Hot API on a webpage for a live editor playground.
|
25 |
|
26 | ### Usage
|
27 |
|
28 | This library is not meant to be used directly, unless you're authoring a build tool like [React Hot Loader](https://github.com/gaearon/react-hot-loader).
|
29 |
|
30 | It only makes sense if your build tool of choice is capable of two things:
|
31 |
|
32 | * emitting next versions of the same module and evaluate them;
|
33 | * passing arbitrary JS objects from previous to the next version of the same module.
|
34 |
|
35 | I am only aware of [Webpack Hot Module Replacement](http://webpack.github.io/docs/hot-module-replacement.html) but eventually other implementations should arise.
|
36 |
|
37 | In which case, here's how you can tranform the source to use it:
|
38 |
|
39 | ##### SomeComponent.js, first run
|
40 |
|
41 | ```javascript
|
42 | var React = require('react');
|
43 |
|
44 | var SomeComponent = React.createClass({
|
45 | render: function () {
|
46 | return <p>Version 1</p>;
|
47 | }
|
48 | });
|
49 |
|
50 | module.exports = SomeComponent;
|
51 |
|
52 |
|
53 |
|
54 | // ================================================
|
55 | // The code you might generate with your build tool
|
56 | // to hide hot reloading mechanics from user:
|
57 |
|
58 | var makeHot = SOME_STORAGE_SHARED_BETWEEN_VERSIONS_OF_SAME_MODULE.makeHot;
|
59 | if (!makeHot) {
|
60 | // On the first run, we will get here
|
61 | makeHot = SOME_STORAGE_SHARED_BETWEEN_VERSIONS_OF_SAME_MODULE.makeHot = require('react-hot-api')(require('react/lib/ReactMount'));
|
62 | }
|
63 |
|
64 | // Will merely register SomeComponent so it can later be patched
|
65 | module.exports = makeHot(module.exports);
|
66 | ```
|
67 |
|
68 | ##### SomeComponent.js, subsequent runs (emitted after user edits the source)
|
69 | ```javascript
|
70 | var React = require('react');
|
71 |
|
72 | var SomeComponent = React.createClass({
|
73 | render: function () {
|
74 | return <p>Version 2</p>;
|
75 | }
|
76 | });
|
77 |
|
78 | module.exports = SomeComponent;
|
79 |
|
80 |
|
81 |
|
82 | // ================================================
|
83 | // The code you might generate with your build tool
|
84 | // to hide hot reloading mechanics from user:
|
85 |
|
86 | var makeHot = SOME_STORAGE_SHARED_BETWEEN_VERSIONS_OF_SAME_MODULE.makeHot;
|
87 | if (!makeHot) {
|
88 | // On the second run, we will *NOT* get here
|
89 | makeHot = SOME_STORAGE_SHARED_BETWEEN_VERSIONS_OF_SAME_MODULE.makeHot = require('react-hot-api')(require('react/lib/ReactMount'));
|
90 | }
|
91 |
|
92 | // Will patch existing SomeComponent with updated methods, force re-rendering and return patched first version
|
93 | module.exports = makeHot(module.exports);
|
94 | ```
|
95 |
|
96 | You may also give user some way to access `makeHot` in case they want to allow hot-reloading for arbitrary classes inside the module:
|
97 |
|
98 | ##### AnonComponents.js
|
99 | ```javascript
|
100 | // The user still doesn't need to know these lines are being inserted by the tool:
|
101 | var module.makeHot = SOME_STORAGE_SHARED_BETWEEN_VERSIONS_OF_SAME_MODULE.makeHot;
|
102 | if (!module.makeHot) {
|
103 | // put the function into some sane place (e.g. module.makeHot) without relying on hidden variables
|
104 | module.makeHot = SOME_STORAGE_SHARED_BETWEEN_VERSIONS_OF_SAME_MODULE.makeHot = require('react-hot-api')(require('react/lib/ReactMount'));
|
105 | }
|
106 | // You might generate the code above with your build tool
|
107 | // to hide hot reloading mechanics from user.
|
108 | // ================================================
|
109 |
|
110 |
|
111 |
|
112 | var React = require('react');
|
113 |
|
114 | function createLabelComponent(str) {
|
115 | var cls = React.createClass({
|
116 | render: function () {
|
117 | return <span>{str}</span>;
|
118 | }
|
119 | });
|
120 |
|
121 | // ... but you may give user freedom to do this:
|
122 | if (module.makeHot) { // we're in development and makeHot is available
|
123 | cls = module.makeHot(cls, str); // use parameter as unique ID for anon class
|
124 | }
|
125 |
|
126 | return cls;
|
127 | }
|
128 |
|
129 | // These will be hot-reloadable:
|
130 | var Foo = createLabelComponent('Foo');
|
131 | var Bar = createLabelComponent('Bar');
|
132 | ```
|
133 |
|
134 | ### Thanks
|
135 |
|
136 | * [Tobias Koppers](https://github.com/sokra) for Webpack and HMR;
|
137 | * [Johannes Lumpe](https://github.com/johanneslumpe) and [Ben Alpert](https://github.com/spicyj) for helping me come up with the original hot reloading approach I'm still using here;
|
138 | * [Omar Skalli](https://github.com/Chetane) for coming up with an approach for forcing tree update that is compatible with ES6 classes [just the moment I needed it most](https://twitter.com/dan_abramov/status/543174410493239297);
|
139 | * [Kyle Mathews](http://github.com/KyleAMathews) for being the first person to actually use hot loader and helping spread the word when it was in initial stages;
|
140 | * [Christopher Chedeau](https://github.com/vjeux) for retweeting my horrendously hacked together proof of concept video, overwhelming response from which gave me the incentive to actually finish this thing;
|
141 | * Bret Victor for making me think live editing should be the norm, although he probably hates what people do after watching his videos.
|