From 4e2c4ac4e29fe3465beee20277d30ccffa3e57db Mon Sep 17 00:00:00 2001 From: Harry Hedger Date: Fri, 22 Apr 2016 00:06:16 -0400 Subject: [PATCH 01/20] Removed starter kits from docs/README.md --- docs/README.md | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/docs/README.md b/docs/README.md index 93c35db3a..23b7fdb39 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,25 +1,5 @@ ### Starter Kits - * [react-hot-boilerplate](https://github.com/gaearon/react-hot-boilerplate) (Bare minimum) -* [react-starter](https://github.com/webpack/react-starter) (react-router, includes production configs) -* [isomorphic-hot-loader](https://github.com/irvinebroque/isomorphic-hot-loader) (react-router, isomorphic) -* [isomorphic-react-template](https://github.com/gpbl/isomorphic-react-template/) (react-router, isomorphic) -* [coffee-react-quickstart](https://github.com/KyleAMathews/coffee-react-quickstart) (react-router, CoffeeScript, Gulp) -* [react-static-boilerplate](https://github.com/koistya/react-static-boilerplate) (static site generator; React, PostCSS, Webpack, BrowserSync) -* [boilerplate-webpack-react](https://github.com/tcoopman/boilerplate-webpack-react) (react-router, isomorphic) -* [este](http://github.com/steida/este) (react-router, isomorphic, Flux, Babel) -* [react-isomorphic-starterkit](https://github.com/RickWong/react-isomorphic-starterkit) (react-router, react-async, isomorphic) -* [yarsk](https://github.com/bradleyboy/yarsk) (Babel, Karma + Mocha, automated publishing to GitHub pages) -* [react-web](https://github.com/darul75/web-react) (Babel, react-router, Alt flux) -* [esnext-quickstart](https://github.com/nkbt/esnext-quickstart) (compile-time ESLint, ES6, Babel, Karma + Jasmine + Coverage) -* [react-webpack-boilerplate](https://github.com/srn/react-webpack-boilerplate) (One-click Heroku deployable, Node.js server) -* [flask-react-boilerplate](https://github.com/alexkuz/flask-react-boilerplate) (One-click Heroku deployable, Flask server + PostgreSQL, Babel, Flummox) -* [react-kickstart](https://github.com/vesparny/react-kickstart) (react-router, mocha + chai + istanbul) -* [react-redux-universal-hot-example](https://github.com/erikras/react-redux-universal-hot-example) (isomorphic, redux, client and server async data fetching, babel, react-router) -* [go-starter-kit](https://github.com/olebedev/go-starter-kit) (hot reloadable golang/react/flummox/css-module isomorphic starter kit) -* [react-fullstack-skeleton](https://github.com/fortruce/react-fullstack-skeleton) (react w/ backend api server) - -Don't be shy, add your own. ### Migrating to 1.0 From 84a1f5a82b4a676fd825196e9dedb68022233d9b Mon Sep 17 00:00:00 2001 From: Harry Hedger Date: Fri, 22 Apr 2016 00:10:42 -0400 Subject: [PATCH 02/20] Removed old documentation. --- docs/README.md | 82 -------------------------------------------------- 1 file changed, 82 deletions(-) diff --git a/docs/README.md b/docs/README.md index 23b7fdb39..77f71aba6 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,88 +1,6 @@ ### Starter Kits * [react-hot-boilerplate](https://github.com/gaearon/react-hot-boilerplate) (Bare minimum) -### Migrating to 1.0 - -React Hot Loader has reached 1.0, and it's a breaking change. When React Hot Loader just started, it used a regex to find `createClass` calls and replace them with its own implementation. This turned out to be a bad idea for a number of reasons: - -* Doesn't work when components are created through wrappers (e.g. [OmniscientJS](http://omniscientjs.github.io)); -* Doesn't work when author calls React differently; -* Causes false positives in React source code comments and elsewhere; -* Most importantly, won't work with ES6 classes that will be future of React. - -Here's how we're solving these problems in 1.0: - -#### Only `module.exports` and its own properties are hot by default - -With 1.0, we no longer parse your sources. Instead, we only now make `module.exports` and its [own properties](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty) hot by default, and only if their prototype declares `render` method or descends from `React.Component`. **If you've been splitting each component in a separate file, that means no change for you here!** This allows us to support exotic wrappers. - -If you use inheritance in React 0.13, base classes will only be opted into hot reloading if they descend from `React.Component` or define `render` method. Otherwise you need to explicitly call `module.makeHot` as described below. - -#### You can make hot anything else via opt-in `module.makeHot` API - -But what if you *want* to have several hot-reloadable components in one file? Or what if you want to export a function creating components, or an object with several components as properties? For that, 1.0 **adds first public API to hot loader: `module.makeHot`**. This method will be present on `module` object if hot loader is enabled, and allows you to make any component hot: - -```js -var Something = React.createClass({ - ... -}; - -if (module.makeHot) { // Won't be true in production - Something = module.makeHot(Something); -} -``` - -Explicit API can also be used inside functions: - -```js -function generateClass(param) { - var Class = return React.createClass({ - ... - }; - - if (module.makeHot) { - Class = module.makeHot(Class, param); - } - - return Class; -} - -``` - -Note the second parameter: `makeHot` needs some way to distinguish components of same type inside on module. By default, it uses `displayName` of given component class, but in case of dynamically generated classes (or if you're not using JSX), you have to provide it yourself. - -### Manual mode (experimental) - -You can now use `react-hot?manual` instead of `react-hot` in Webpack config to turn on manual mode. In manual mode, “accepting” hot updates is up to you; modules won't accept themselves automatically. This can be used, for example, to put reloading logic on very top of the application and [hot-reload routes as well as components](https://github.com/rackt/react-router/pull/606#issuecomment-66936975). It will also work better when you have a lot of modules that export component-generating functions because updates will propagate to the top. (Don't worry if you don't understand this; it's just something experimental you might want to try to integrate hot reloading deeper into your app.) - -### Usage with external React - -If you're using external standalone React bundle instead of NPM package, Hot Loader will fail because it relies on `react/lib/ReactMount` which is not exposed in precompiled React. It needs `ReactMount` to keep track of mounted React component instances on the page. However, you can supply your own root instance provider: - -```js -// Your app's index.js - -var React = require('react'), - router = require('./router'); - -var rootInstance = null; - -router.run(function (Handler, state) { - rootInstance = React.render(, document.body); -}); - -if (module.hot) { - require('react-hot-loader/Injection').RootInstanceProvider.injectProvider({ - getRootInstances: function () { - // Help React Hot Loader figure out the root component instances on the page: - return [rootInstance]; - } - }); -} -``` - -You'll only need this if you [use a precompiled version of React](https://github.com/gaearon/react-hot-loader/issues/53). If you use React NPM package, this is not necessary. You should generally use React NPM package unless you have good reason not to. - ### Source Maps If you use `devtool: 'source-map'` (or its equivalent), source maps will be emitted to hide hot reloading code. From 5d551eb0e5c3330f0ae5ef74db22c5da7260d5fb Mon Sep 17 00:00:00 2001 From: Harry Hedger Date: Fri, 22 Apr 2016 01:20:21 -0400 Subject: [PATCH 03/20] Migration guide. Announcement on main README --- README.md | 20 ++++++++++----- docs/README.md | 68 ++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 77 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 6be5ccede..750c89a3e 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,22 @@ # React Hot Loader [![npm package](https://img.shields.io/npm/v/react-hot-loader.svg?style=flat-square)](https://www.npmjs.org/package/react-hot-loader) +**React Hot Loader 3 has arrived.** + +It fixes some long-standing issues with both React Hot Loader and React Transform. **It is intended as a replacement for both.** + +Some nice things about it: + +* Editing functional components preserves state +* Works great with higher order components +* Requires little configuration +* Automatically disabled in production +* Works with or without Babel (you can remove `react-hot-loader/babel` from `.babelrc` and instead add `react-hot-loader/webpack` to `loaders`) + This is a **stable for daily use in development** implementation of [React live code editing](http://www.youtube.com/watch?v=pw4fKkyPPg8). * Get inspired by a **[demo video](https://vimeo.com/100010922)** and **[try the live demo](http://gaearon.github.io/react-hot-loader/)**. -* Read **[the integration walkthrough](http://gaearon.github.io/react-hot-loader/getstarted/).** +* Read **[the Getting Started guide](http://gaearon.github.io/react-hot-loader/getstarted/).** * Use **[one of the starter kits](https://github.com/gaearon/react-hot-loader/tree/master/docs#starter-kits)** for your next React project. @@ -27,14 +39,10 @@ To use React Hot Loader in an existing project, you need to * enable Hot Module Replacement, which is a Webpack feature; * configure Webpack to use React Hot Loader for JS or JSX files. -These steps are covered by **[the walkthrough](http://gaearon.github.io/react-hot-loader/getstarted/)**. +These steps are covered by **[the Getting Started guide](http://gaearon.github.io/react-hot-loader/getstarted/)**. If you'd rather stay with **Browserify**, check out **[LiveReactload](https://github.com/milankinen/livereactload)** by Matti Lankinen. -## Flux - -**[Redux](https://github.com/gaearon/redux)** is a Flux implementation that supports hot reloading of everything out of the box. Read **[The Evolution of Flux Frameworks](https://medium.com/@dan_abramov/the-evolution-of-flux-frameworks-6c16ad26bb31)** for some context around its creation. - ## React Native You can use React Hot Loader to tweak a React Native application. Check out **[react-native-webpack-server](https://github.com/mjohnston/react-native-webpack-server)** by Michael Johnston. diff --git a/docs/README.md b/docs/README.md index 77f71aba6..a3abe338a 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,6 +1,68 @@ -### Starter Kits +### Starter Kit * [react-hot-boilerplate](https://github.com/gaearon/react-hot-boilerplate) (Bare minimum) +### Migration to 3.0 +- If you're using Babel and ES6, remove the `react-hot` loader from any loaders in your Webpack config, and add `react-hot-loader/babel` to the `plugins` section of your `.babelrc`: + +```js +{ + "presets": ["es2015-loose", "stage-0", "react"], + "plugins": ["react-hot-loader/babel"] +} +``` + +- If you're *not* using Babel, or you're using Babel without ES6, replace the `react-hot` loader in your Webpack config with `react-hot-loader/webpack`: + +```js +{ + test: /\.js$/, + loaders: ['react-hot', 'babel'], + include: path.join(__dirname, '..', '..', 'src') +} + +// becomes +{ + test: /\.js$/, + loaders: ['react-hot-loader/webpack', 'babel'], + include: path.join(__dirname, '..', '..', 'src') +} +``` + +- 'react-hot-loader/patch' should be placed at the top of the `entry` section in your Webpack config. An error will occur if any code runs before `react-hot-loader/patch` has, so put it in the first position. + +- `` - AppContainer is a component that handles module reloading, as well as error handling. The root component of your app should be nested in AppContainer as a child. When in production, AppContainer is automatically disabled, and simply returns its children. + +- React Hot Loader 3 does not hide the hot module replacement API, so the following needs to be added below wherever you call `ReactDOM.render` in your app: + +```js +import React from 'react' +import ReactDOM from 'react-dom' +import { AppContainer } from 'react-hot-loader' +import App from './containers/App' + +ReactDOM.render( + + + , + document.getElementById('root') +); + +// Hot Module Replacement API +if (module.hot) { + module.hot.accept('./containers/App', () => { + render( + + + + />, + document.getElementById('root') + ); + }); +} +``` + +You can also check out [this commit for the migration of a TodoMVC app from 1.0 to 3.0.](https://github.com/gaearon/redux-devtools/commit/64f58b7010a1b2a71ad16716eb37ac1031f93915) + ### Source Maps If you use `devtool: 'source-map'` (or its equivalent), source maps will be emitted to hide hot reloading code. @@ -8,7 +70,3 @@ If you use `devtool: 'source-map'` (or its equivalent), source maps will be emit Source maps slow down your project. Use `devtool: 'eval'` for best build performance. Hot reloading code is just one line in the beginning and one line in the end of each module so you might not need source maps at all. - -### React Hot API - -If you're authoring a build tool, you might be interested to hear that React Hot Loader brains have been extracted into runtime-agnostic [React Hot API](https://github.com/gaearon/react-hot-api). React Hot Loader just binds that API to Webpack runtime, but you can implement yours too. From eec65321fbd63a3cb3201a33fb5413d51d7fa365 Mon Sep 17 00:00:00 2001 From: Harry Hedger Date: Fri, 22 Apr 2016 01:31:40 -0400 Subject: [PATCH 04/20] Cleaned up the top of README.md --- README.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 750c89a3e..98adab5dd 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,10 @@ -# React Hot Loader [![npm package](https://img.shields.io/npm/v/react-hot-loader.svg?style=flat-square)](https://www.npmjs.org/package/react-hot-loader) +# React Hot Loader 3 [![npm package](https://img.shields.io/npm/v/react-hot-loader.svg?style=flat-square)](https://www.npmjs.org/package/react-hot-loader) -**React Hot Loader 3 has arrived.** +### React Hot Loader 3 has arrived! -It fixes some long-standing issues with both React Hot Loader and React Transform. **It is intended as a replacement for both.** +It fixes some long-standing issues with both React Hot Loader and React Transform. + +**It is intended as a replacement for both.** Some nice things about it: @@ -12,6 +14,10 @@ Some nice things about it: * Automatically disabled in production * Works with or without Babel (you can remove `react-hot-loader/babel` from `.babelrc` and instead add `react-hot-loader/webpack` to `loaders`) +Check out [the Migration to 3.0 guide](https://github.com/gaearon/react-hot-loader/tree/master/docs#migration-to-30) to learn how to migrate your app to 3.0. + +### Learn + This is a **stable for daily use in development** implementation of [React live code editing](http://www.youtube.com/watch?v=pw4fKkyPPg8). * Get inspired by a **[demo video](https://vimeo.com/100010922)** and **[try the live demo](http://gaearon.github.io/react-hot-loader/)**. From b29375cb09c2866523f0c5743f4f26ad18377a8a Mon Sep 17 00:00:00 2001 From: Gadi Cohen Date: Mon, 25 Apr 2016 21:04:44 +0200 Subject: [PATCH 05/20] docs: early draft --- docs/README.md | 226 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 151 insertions(+), 75 deletions(-) diff --git a/docs/README.md b/docs/README.md index 93c35db3a..a1cd9fbfa 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,116 +1,192 @@ +# React Hot Loader v3 + +**_Draft docs_** + +## Intro + +React Hot Loader lets us modify our React components and see the changes in realtime. No more waiting for your entire project to rebuild every time you save a file; your webpage stays loaded and the modified component updates instantly. + +XXX do we even need an intro here? What will be here vs main README? e.g. +* animated gif (should definitely be main README) +* link to reacteurope video + +**Differences from Past Approaches and Issues Solved** + +First we had React Hot Loader, which was superseded by React Transform. Each time we solved earlier issues but were faced with new ones. RHLv3 solves all these issues with a more sustainable approach, and is intended as a replacement for both: + +* Preserves state in functional components +* Little configuration required +* Disabled in production +* Works with or without Babel +* Everything you need in a single repo + +For more information on the evolution of approaches taken, see [](https://medium.com/@dan_abramov/hot-reloading-in-react-1140438583bf)https://medium.com/@dan_abramov/hot-reloading-in-react-1140438583bf. + +## Boilerplate Example + +What follows is a 3-step guide to integrating React Hot Loader into your current project. Alternatively, you can also clone the boilerplate, for a quick start on a fresh app with everything working out-of-the-box. + +[](https://github.com/gaearon/react-hot-boilerplate/)https://github.com/gaearon/react-hot-boilerplate/ + ### Starter Kits -* [react-hot-boilerplate](https://github.com/gaearon/react-hot-boilerplate) (Bare minimum) -* [react-starter](https://github.com/webpack/react-starter) (react-router, includes production configs) -* [isomorphic-hot-loader](https://github.com/irvinebroque/isomorphic-hot-loader) (react-router, isomorphic) -* [isomorphic-react-template](https://github.com/gpbl/isomorphic-react-template/) (react-router, isomorphic) -* [coffee-react-quickstart](https://github.com/KyleAMathews/coffee-react-quickstart) (react-router, CoffeeScript, Gulp) -* [react-static-boilerplate](https://github.com/koistya/react-static-boilerplate) (static site generator; React, PostCSS, Webpack, BrowserSync) -* [boilerplate-webpack-react](https://github.com/tcoopman/boilerplate-webpack-react) (react-router, isomorphic) -* [este](http://github.com/steida/este) (react-router, isomorphic, Flux, Babel) -* [react-isomorphic-starterkit](https://github.com/RickWong/react-isomorphic-starterkit) (react-router, react-async, isomorphic) -* [yarsk](https://github.com/bradleyboy/yarsk) (Babel, Karma + Mocha, automated publishing to GitHub pages) -* [react-web](https://github.com/darul75/web-react) (Babel, react-router, Alt flux) -* [esnext-quickstart](https://github.com/nkbt/esnext-quickstart) (compile-time ESLint, ES6, Babel, Karma + Jasmine + Coverage) -* [react-webpack-boilerplate](https://github.com/srn/react-webpack-boilerplate) (One-click Heroku deployable, Node.js server) -* [flask-react-boilerplate](https://github.com/alexkuz/flask-react-boilerplate) (One-click Heroku deployable, Flask server + PostgreSQL, Babel, Flummox) -* [react-kickstart](https://github.com/vesparny/react-kickstart) (react-router, mocha + chai + istanbul) -* [react-redux-universal-hot-example](https://github.com/erikras/react-redux-universal-hot-example) (isomorphic, redux, client and server async data fetching, babel, react-router) -* [go-starter-kit](https://github.com/olebedev/go-starter-kit) (hot reloadable golang/react/flummox/css-module isomorphic starter kit) -* [react-fullstack-skeleton](https://github.com/fortruce/react-fullstack-skeleton) (react w/ backend api server) +**XXX: from old doc, but we only want ones that are up to date with RHLv3** + +## Integrating into your own App + +### Step 1/3: Enabling Hot Module Replacement (HMR) + +HMR allows us to replace modules in-place without restarting the server, here's how you can enable it: -Don't be shy, add your own. +**Webpack** -### Migrating to 1.0 +* Create a development Webpack config separate from production one +* Add HotModuleReplacementPlugin to development Webpack config +* If you only render on the client, consider using WebpackDevServer + * Easier to set up + * Enable hot: true and add its entry points +* If you use server rendering, consider using Express server + webpack-dev-middleware +* More work but also more control +* Show how to add webpack-dev-middleware and its entry point -React Hot Loader has reached 1.0, and it's a breaking change. When React Hot Loader just started, it used a regex to find `createClass` calls and replace them with its own implementation. This turned out to be a bad idea for a number of reasons: +**XXX cleanup, details** -* Doesn't work when components are created through wrappers (e.g. [OmniscientJS](http://omniscientjs.github.io)); -* Doesn't work when author calls React differently; -* Causes false positives in React source code comments and elsewhere; -* Most importantly, won't work with ES6 classes that will be future of React. +**Browserify** -Here's how we're solving these problems in 1.0: +If you have this setup working, please consider submitting instructions as a PR. -#### Only `module.exports` and its own properties are hot by default +**Meteor** -With 1.0, we no longer parse your sources. Instead, we only now make `module.exports` and its [own properties](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty) hot by default, and only if their prototype declares `render` method or descends from `React.Component`. **If you've been splitting each component in a separate file, that means no change for you here!** This allows us to support exotic wrappers. +* If you're using [webpack:webpack](https://atmospherejs.com/webpack/webpack) you can follow the webpack instructions or ask for help in [this](https://forums.meteor.com/t/use-webpack-with-meteor-simply-by-adding-packages-meteor-webpack-1-0-is-out/18819) forum post. -If you use inheritance in React 0.13, base classes will only be opted into hot reloading if they descend from `React.Component` or define `render` method. Otherwise you need to explicitly call `module.makeHot` as described below. +* Otherwise, for HMR in "native" Meteor, type: `meteor remove ecmascript && meteor add gadicc:ecmascript-hot` or see the [README](https://github.com/gadicc/meteor-react-hotloader#readme) for more details. There are also some Meteor-specific RHLv3 install instructions [here](https://github.com/gadicc/meteor-react-hotloader/blob/master/docs/React_Hotloading.md). -#### You can make hot anything else via opt-in `module.makeHot` API +### Step 2/3: Using HMR to replace the root component -But what if you *want* to have several hot-reloadable components in one file? Or what if you want to export a function creating components, or an object with several components as properties? For that, 1.0 **adds first public API to hot loader: `module.makeHot`**. This method will be present on `module` object if hot loader is enabled, and allows you to make any component hot: +When the HMR runtime receives an updated module, it first checks to see if the module knows how to update itself, and then ascends the import/require chain looking for a parent module that can accept the update. We want our root component to be able to accept an update from any child component. + +If your client entry point looks like this: ```js -var Something = React.createClass({ - ... -}; +import React from 'react'; +import { render } from 'react-dom'; +import RootContainer from './containers/rootContainer.js'; -if (module.makeHot) { // Won't be true in production - Something = module.makeHot(Something); -} +render(, document.elementById('react-root'); ``` - -Explicit API can also be used inside functions: +you would add the following code to accept changes to RootContainer _or any of it's descendants_. ```js -function generateClass(param) { - var Class = return React.createClass({ - ... - }; + if (module.hot) { + module.hot.accept('./containers/rootContainer.js', function() { + var NextRootContainer = require('./containers/rootContainer.js'; + render(, document.elementById('react-root'); + } + } +``` +Note, with no further steps, this enough to hotload changes to React components, but state will not be preserved. If you externalize all your state in a state store like Redux, this might be enough. - if (module.makeHot) { - Class = module.makeHot(Class, param); - } +### Step 3/3: Adding React Hot Loader to preserve state - return Class; -} +The final step adds adds `react-hot-loader` to our project to preserve _component state_ across hot loads. -``` +1. Install the package: -Note the second parameter: `makeHot` needs some way to distinguish components of same type inside on module. By default, it uses `displayName` of given component class, but in case of dynamically generated classes (or if you're not using JSX), you have to provide it yourself. + ```sh + npm install --save-dev react-hot-loader + ``` +1. Add the package to your config. -### Manual mode (experimental) + a. If you use Babel, modify your `.babelrc` to ensure it includes at least: -You can now use `react-hot?manual` instead of `react-hot` in Webpack config to turn on manual mode. In manual mode, “accepting” hot updates is up to you; modules won't accept themselves automatically. This can be used, for example, to put reloading logic on very top of the application and [hot-reload routes as well as components](https://github.com/rackt/react-router/pull/606#issuecomment-66936975). It will also work better when you have a lot of modules that export component-generating functions because updates will propagate to the top. (Don't worry if you don't understand this; it's just something experimental you might want to try to integrate hot reloading deeper into your app.) + ```js + { + "plugins": [ "react-hot-loader/babel" ] + } + ``` + b. Alternatively, in Webpack, add `react-hot-loader/webpack` to your loaders -### Usage with external React + ```js + XXX + ``` -If you're using external standalone React bundle instead of NPM package, Hot Loader will fail because it relies on `react/lib/ReactMount` which is not exposed in precompiled React. It needs `ReactMount` to keep track of mounted React component instances on the page. However, you can supply your own root instance provider: +1. Add following line to the top of your main entry point: + ```js + import 'react-hot-loader/patch'; + ``` -```js -// Your app's index.js +1. Wrap your `` inside of an ``: + + ```js + import { AppContainer } from 'react-hot-loader'; + render(, + document.getElementById('react-root'); + ``` + **XXX pending [gaearon/react hot loader#244](https://github.com/gaearon/react-hot-loader/issues/244)** -var React = require('react'), - router = require('./router'); + You should do this for both instances, e.g. your original mount and your mount code inside of the `module.hot.accept()` function. `` must wrap only a single, React component. -var rootInstance = null; +That's it! -router.run(function (Handler, state) { - rootInstance = React.render(, document.body); -}); +## Putting it all together + +If you followed all the steps your app's main entry point should now look something like this: + +```js +import 'react-hot-loader/patch'; +import React from 'react'; +import { render } from 'react-dom'; +import { AppContainer } from 'react-hot-loader'; +import RootContainer from './containers/rootContainer.js'; + +render(, + document.getElementById('react-root'); if (module.hot) { - require('react-hot-loader/Injection').RootInstanceProvider.injectProvider({ - getRootInstances: function () { - // Help React Hot Loader figure out the root component instances on the page: - return [rootInstance]; - } - }); + module.hot.accept('./containers/rootContainer.js', function() { + var NextRootContainer = require('./containers/rootContainer.js'; + render(, + document.getElementById('react-root'); + } } ``` -You'll only need this if you [use a precompiled version of React](https://github.com/gaearon/react-hot-loader/issues/53). If you use React NPM package, this is not necessary. You should generally use React NPM package unless you have good reason not to. +### Checking that everything is working properly -### Source Maps +**XXX** -If you use `devtool: 'source-map'` (or its equivalent), source maps will be emitted to hide hot reloading code. +## Troubleshooting -Source maps slow down your project. Use `devtool: 'eval'` for best build performance. +**XXX could/should be in another file** -Hot reloading code is just one line in the beginning and one line in the end of each module so you might not need source maps at all. +## Tips and Tricks + +**XXX could/should be in another file** + +**How to get an error in your console too:** + +The redbox errors are great to clearly see rendering issues, and avoiding an uncaught error from breaking your app. But there are some advantages to a thrown error in the console too, like filename resolution via sourcemaps, and click-to-open. To get the best of both worlds, modify your app entry point as follows: + +```js +import Redbox from 'redbox-react'; + +const consoleErrorReporter = ({error}) => { + // We throw in a different context, so the app still doesn't break! + setTimeout(() => { throw error; }); + return ; +}; +consoleErrorReporter.propTypes = { + error: React.PropTypes.error +}; + +render( + + + , + document.getElementById('react-root') +); +``` -### React Hot API +## Where to ask for Help -If you're authoring a build tool, you might be interested to hear that React Hot Loader brains have been extracted into runtime-agnostic [React Hot API](https://github.com/gaearon/react-hot-api). React Hot Loader just binds that API to Webpack runtime, but you can implement yours too. +**XXX** \ No newline at end of file From 27bd7e055d2ebd1546a4927214182351bfe9ca90 Mon Sep 17 00:00:00 2001 From: Gadi Cohen Date: Mon, 25 Apr 2016 21:56:45 +0200 Subject: [PATCH 06/20] fix broken render statements --- docs/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/README.md b/docs/README.md index a1cd9fbfa..b1414dfe1 100644 --- a/docs/README.md +++ b/docs/README.md @@ -72,7 +72,7 @@ import React from 'react'; import { render } from 'react-dom'; import RootContainer from './containers/rootContainer.js'; -render(, document.elementById('react-root'); +render(, document.elementById('react-root')); ``` you would add the following code to accept changes to RootContainer _or any of it's descendants_. @@ -80,7 +80,7 @@ you would add the following code to accept changes to RootContainer _or any of i if (module.hot) { module.hot.accept('./containers/rootContainer.js', function() { var NextRootContainer = require('./containers/rootContainer.js'; - render(, document.elementById('react-root'); + render(, document.elementById('react-root')); } } ``` @@ -120,7 +120,7 @@ The final step adds adds `react-hot-loader` to our project to preserve _componen ```js import { AppContainer } from 'react-hot-loader'; render(, - document.getElementById('react-root'); + document.getElementById('react-root')); ``` **XXX pending [gaearon/react hot loader#244](https://github.com/gaearon/react-hot-loader/issues/244)** @@ -146,7 +146,7 @@ if (module.hot) { module.hot.accept('./containers/rootContainer.js', function() { var NextRootContainer = require('./containers/rootContainer.js'; render(, - document.getElementById('react-root'); + document.getElementById('react-root')); } } ``` From 8ab36b5026d792ef2c4e433da32a533a131d5342 Mon Sep 17 00:00:00 2001 From: Gadi Cohen Date: Tue, 26 Apr 2016 06:13:33 +0200 Subject: [PATCH 07/20] fix closing parenthesis on require statements --- docs/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/README.md b/docs/README.md index b1414dfe1..0d3f7fe10 100644 --- a/docs/README.md +++ b/docs/README.md @@ -79,7 +79,7 @@ you would add the following code to accept changes to RootContainer _or any of i ```js if (module.hot) { module.hot.accept('./containers/rootContainer.js', function() { - var NextRootContainer = require('./containers/rootContainer.js'; + var NextRootContainer = require('./containers/rootContainer.js'); render(, document.elementById('react-root')); } } @@ -144,7 +144,7 @@ render(, if (module.hot) { module.hot.accept('./containers/rootContainer.js', function() { - var NextRootContainer = require('./containers/rootContainer.js'; + var NextRootContainer = require('./containers/rootContainer.js'); render(, document.getElementById('react-root')); } From d0634a45025c14b9d18ada50672e103e103c4f26 Mon Sep 17 00:00:00 2001 From: Gadi Cohen Date: Tue, 26 Apr 2016 07:36:03 +0200 Subject: [PATCH 08/20] require().default for ES6 modules --- docs/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/README.md b/docs/README.md index 0d3f7fe10..5048258d1 100644 --- a/docs/README.md +++ b/docs/README.md @@ -79,7 +79,7 @@ you would add the following code to accept changes to RootContainer _or any of i ```js if (module.hot) { module.hot.accept('./containers/rootContainer.js', function() { - var NextRootContainer = require('./containers/rootContainer.js'); + var NextRootContainer = require('./containers/rootContainer.js').default; render(, document.elementById('react-root')); } } @@ -144,7 +144,7 @@ render(, if (module.hot) { module.hot.accept('./containers/rootContainer.js', function() { - var NextRootContainer = require('./containers/rootContainer.js'); + var NextRootContainer = require('./containers/rootContainer.js').default; render(, document.getElementById('react-root')); } From e043b10a6d98dfe5c65e57363d4593585f09c614 Mon Sep 17 00:00:00 2001 From: David Tsai Date: Tue, 26 Apr 2016 00:03:52 -0700 Subject: [PATCH 09/20] Adding additional clarification to RHL3 docs along with JSX highlighting in code blocks --- docs/README.md | 87 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 65 insertions(+), 22 deletions(-) diff --git a/docs/README.md b/docs/README.md index 5048258d1..7187b866e 100644 --- a/docs/README.md +++ b/docs/README.md @@ -20,7 +20,8 @@ First we had React Hot Loader, which was superseded by React Transform. Each ti * Works with or without Babel * Everything you need in a single repo -For more information on the evolution of approaches taken, see [](https://medium.com/@dan_abramov/hot-reloading-in-react-1140438583bf)https://medium.com/@dan_abramov/hot-reloading-in-react-1140438583bf. +For more information on the evolution of approaches taken +To learn more about the evolution of prior approaches, read ["Hot Reloading in React"](https://medium.com/@dan_abramov/hot-reloading-in-react-1140438583bf) by [Dan Abramov](https://medium.com/@dan_abramov) ## Boilerplate Example @@ -107,7 +108,21 @@ The final step adds adds `react-hot-loader` to our project to preserve _componen b. Alternatively, in Webpack, add `react-hot-loader/webpack` to your loaders ```js - XXX + // webpackConfig.js + + // TODO: Would love some help showing the shape of the webpack config without + // overwhelming users either - just want it to be familiar enough. I suppose we could + // also declare a variable and assign the require statement to it? (Just an idea) + devtool: ..., + entry: [...], + module: { + loaders: [{ + test: /\.js$/, + loaders: ['react-hot-loader/webpack', 'babel'], + include: path.join(__dirname, 'src') + }] + } + ``` 1. Add following line to the top of your main entry point: @@ -119,8 +134,13 @@ The final step adds adds `react-hot-loader` to our project to preserve _componen ```js import { AppContainer } from 'react-hot-loader'; - render(, - document.getElementById('react-root')); + import RootContainer from './containers/rootContainer.js'; + + render(( + + + + ), document.getElementById('react-root')); ``` **XXX pending [gaearon/react hot loader#244](https://github.com/gaearon/react-hot-loader/issues/244)** @@ -128,26 +148,47 @@ The final step adds adds `react-hot-loader` to our project to preserve _componen That's it! -## Putting it all together +## Putting it all together -If you followed all the steps your app's main entry point should now look something like this: +If you've gotten this far - you're almost done! But before showing you what your app's +main entry point might look like, let's clarify a few things. -```js +`AppContainer` +> `AppContainer` is a component provided by *this* library (`react-hot-loader`), it serves to +wrap your entire app in order to provide hot reloading goodness! + +`RootContainer` +> On the other hand, `RootContainer` represents any application's top-level component, prior +to implementing the `AppContainer` mentioned above. Keep in mind that this can be substituted +for an existing wrapper/parent component. + +Your application's main entry point might look like the code presented below. Notice that +we are targeting and subsequently rendering into a particular DOM element's id (conveniently named `react-root`). + +```javascript import 'react-hot-loader/patch'; import React from 'react'; import { render } from 'react-dom'; -import { AppContainer } from 'react-hot-loader'; +// See notes above re: AppContainer and RootContainer +import { AppContainer } from 'react-hot-loader' import RootContainer from './containers/rootContainer.js'; -render(, - document.getElementById('react-root'); +render(( + + + +), document.getElementById('react-root')); if (module.hot) { - module.hot.accept('./containers/rootContainer.js', function() { - var NextRootContainer = require('./containers/rootContainer.js').default; - render(, - document.getElementById('react-root')); - } + module.hot.accept('./containers/rootContainer.js', () => { + const NextRootContainer = require('./containers/rootContainer.js'); + + render(( + + + + ), document.getElementById('react-root')); + }) } ``` @@ -165,9 +206,11 @@ if (module.hot) { **How to get an error in your console too:** -The redbox errors are great to clearly see rendering issues, and avoiding an uncaught error from breaking your app. But there are some advantages to a thrown error in the console too, like filename resolution via sourcemaps, and click-to-open. To get the best of both worlds, modify your app entry point as follows: +The `Redbox` errors are great to clearly see rendering issues, and avoiding an uncaught error from breaking your app. +But there are some advantages to a thrown error in the console too, like filename resolution via sourcemaps, and click-to-open. +To get the best of both worlds, modify your app entry point as follows: -```js +```jsx import Redbox from 'redbox-react'; const consoleErrorReporter = ({error}) => { @@ -175,18 +218,18 @@ const consoleErrorReporter = ({error}) => { setTimeout(() => { throw error; }); return ; }; + consoleErrorReporter.propTypes = { error: React.PropTypes.error }; -render( +render(( - , - document.getElementById('react-root') -); + +), document.getElementById('react-root')); ``` ## Where to ask for Help -**XXX** \ No newline at end of file +**XXX** From 77dd1db4da4cdce679485936767125218300393d Mon Sep 17 00:00:00 2001 From: David Tsai Date: Tue, 26 Apr 2016 00:12:13 -0700 Subject: [PATCH 10/20] Updating syntax in example(s) to be consistent ES6 --- docs/README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/README.md b/docs/README.md index 7187b866e..6aaf27764 100644 --- a/docs/README.md +++ b/docs/README.md @@ -68,7 +68,7 @@ When the HMR runtime receives an updated module, it first checks to see if the m If your client entry point looks like this: -```js +```jsx import React from 'react'; import { render } from 'react-dom'; import RootContainer from './containers/rootContainer.js'; @@ -77,10 +77,10 @@ render(, document.elementById('react-root')); ``` you would add the following code to accept changes to RootContainer _or any of it's descendants_. -```js +```jsx if (module.hot) { - module.hot.accept('./containers/rootContainer.js', function() { - var NextRootContainer = require('./containers/rootContainer.js').default; + module.hot.accept('./containers/rootContainer.js', () => { + const NextRootContainer = require('./containers/rootContainer.js').default; render(, document.elementById('react-root')); } } @@ -94,7 +94,7 @@ The final step adds adds `react-hot-loader` to our project to preserve _componen 1. Install the package: ```sh - npm install --save-dev react-hot-loader + $ npm install --save-dev react-hot-loader ``` 1. Add the package to your config. @@ -132,7 +132,7 @@ The final step adds adds `react-hot-loader` to our project to preserve _componen 1. Wrap your `` inside of an ``: - ```js + ```jsx import { AppContainer } from 'react-hot-loader'; import RootContainer from './containers/rootContainer.js'; @@ -165,7 +165,7 @@ for an existing wrapper/parent component. Your application's main entry point might look like the code presented below. Notice that we are targeting and subsequently rendering into a particular DOM element's id (conveniently named `react-root`). -```javascript +```jsx import 'react-hot-loader/patch'; import React from 'react'; import { render } from 'react-dom'; From 1a7f493d07c20b5c8547b0f4cc8db7fb187412b5 Mon Sep 17 00:00:00 2001 From: Gadi Cohen Date: Tue, 26 Apr 2016 15:14:07 +0200 Subject: [PATCH 11/20] restore original README so we can cleanly merge in hedgerh's changes --- docs/README.md | 279 +++++++++++++++---------------------------------- 1 file changed, 82 insertions(+), 197 deletions(-) diff --git a/docs/README.md b/docs/README.md index 6aaf27764..3801826ae 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,235 +1,120 @@ -# React Hot Loader v3 - -**_Draft docs_** - -## Intro - -React Hot Loader lets us modify our React components and see the changes in realtime. No more waiting for your entire project to rebuild every time you save a file; your webpage stays loaded and the modified component updates instantly. - -XXX do we even need an intro here? What will be here vs main README? e.g. -* animated gif (should definitely be main README) -* link to reacteurope video - -**Differences from Past Approaches and Issues Solved** - -First we had React Hot Loader, which was superseded by React Transform. Each time we solved earlier issues but were faced with new ones. RHLv3 solves all these issues with a more sustainable approach, and is intended as a replacement for both: +### Starter Kits -* Preserves state in functional components -* Little configuration required -* Disabled in production -* Works with or without Babel -* Everything you need in a single repo +* [react-hot-boilerplate](https://github.com/gaearon/react-hot-boilerplate) (Bare minimum) +* [react-starter](https://github.com/webpack/react-starter) (react-router, includes production configs) +* [react-tape](https://github.com/fc-io/react-tape) (Babel, blue-tape, css-loader, html-webpack-plugin, production config) +* [isomorphic-hot-loader](https://github.com/irvinebroque/isomorphic-hot-loader) (react-router, isomorphic) +* [isomorphic-react-template](https://github.com/gpbl/isomorphic-react-template/) (react-router, isomorphic) +* [coffee-react-quickstart](https://github.com/KyleAMathews/coffee-react-quickstart) (react-router, CoffeeScript, Gulp) +* [react-static-boilerplate](https://github.com/koistya/react-static-boilerplate) (static site generator; React, PostCSS, Webpack, BrowserSync) +* [boilerplate-webpack-react](https://github.com/tcoopman/boilerplate-webpack-react) (react-router, isomorphic) +* [este](http://github.com/steida/este) (react-router, isomorphic, Flux, Babel) +* [react-isomorphic-starterkit](https://github.com/RickWong/react-isomorphic-starterkit) (react-router, react-async, isomorphic) +* [yarsk](https://github.com/bradleyboy/yarsk) (Babel, Karma + Mocha, automated publishing to GitHub pages) +* [react-web](https://github.com/darul75/web-react) (Babel, react-router, Alt flux) +* [esnext-quickstart](https://github.com/nkbt/esnext-quickstart) (compile-time ESLint, ES6, Babel, Karma + Jasmine + Coverage) +* [react-webpack-boilerplate](https://github.com/srn/react-webpack-boilerplate) (One-click Heroku deployable, Node.js server) +* [flask-react-boilerplate](https://github.com/alexkuz/flask-react-boilerplate) (One-click Heroku deployable, Flask server + PostgreSQL, Babel, Flummox) +* [react-kickstart](https://github.com/vesparny/react-kickstart) (react-router, mocha + chai + istanbul) +* [react-redux-universal-hot-example](https://github.com/erikras/react-redux-universal-hot-example) (isomorphic, redux, client and server async data fetching, babel, react-router) +* [go-starter-kit](https://github.com/olebedev/go-starter-kit) (hot reloadable golang/react/flummox/css-module isomorphic starter kit) +* [react-fullstack-skeleton](https://github.com/fortruce/react-fullstack-skeleton) (react w/ backend api server) +* [react-cordova-boilerplate](https://github.com/unimonkiez/react-cordova-boilerplate) (react with react-router, redux, hot loader and server-rendering for cordova) +* [react-hot-boilerplate-ts](https://github.com/wmaurer/react-hot-boilerplate-ts) (hot reloadable typescript starter kit) +* [Megatome](https://github.com/Levelmoney/generator-megatome) (Yeoman generator w/ dynamic switchable rendering on Node/browser, react-router, babel, isomophic, an easy config building environments, bring-your-own-data-model and docker) -For more information on the evolution of approaches taken -To learn more about the evolution of prior approaches, read ["Hot Reloading in React"](https://medium.com/@dan_abramov/hot-reloading-in-react-1140438583bf) by [Dan Abramov](https://medium.com/@dan_abramov) +Don't be shy, add your own. -## Boilerplate Example +### Migrating to 1.0 -What follows is a 3-step guide to integrating React Hot Loader into your current project. Alternatively, you can also clone the boilerplate, for a quick start on a fresh app with everything working out-of-the-box. +React Hot Loader has reached 1.0, and it's a breaking change. When React Hot Loader just started, it used a regex to find `createClass` calls and replace them with its own implementation. This turned out to be a bad idea for a number of reasons: -[](https://github.com/gaearon/react-hot-boilerplate/)https://github.com/gaearon/react-hot-boilerplate/ +* Doesn't work when components are created through wrappers (e.g. [OmniscientJS](http://omniscientjs.github.io)); +* Doesn't work when author calls React differently; +* Causes false positives in React source code comments and elsewhere; +* Most importantly, won't work with ES6 classes that will be future of React. -### Starter Kits +Here's how we're solving these problems in 1.0: -**XXX: from old doc, but we only want ones that are up to date with RHLv3** +#### Only `module.exports` and its own properties are hot by default -## Integrating into your own App +With 1.0, we no longer parse your sources. Instead, we only now make `module.exports` and its [own properties](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty) hot by default, and only if their prototype declares `render` method or descends from `React.Component`. **If you've been splitting each component in a separate file, that means no change for you here!** This allows us to support exotic wrappers. -### Step 1/3: Enabling Hot Module Replacement (HMR) +If you use inheritance in React 0.13, base classes will only be opted into hot reloading if they descend from `React.Component` or define `render` method. Otherwise you need to explicitly call `module.makeHot` as described below. -HMR allows us to replace modules in-place without restarting the server, here's how you can enable it: +#### You can make hot anything else via opt-in `module.makeHot` API -**Webpack** +But what if you *want* to have several hot-reloadable components in one file? Or what if you want to export a function creating components, or an object with several components as properties? For that, 1.0 **adds first public API to hot loader: `module.makeHot`**. This method will be present on `module` object if hot loader is enabled, and allows you to make any component hot: -* Create a development Webpack config separate from production one -* Add HotModuleReplacementPlugin to development Webpack config -* If you only render on the client, consider using WebpackDevServer - * Easier to set up - * Enable hot: true and add its entry points -* If you use server rendering, consider using Express server + webpack-dev-middleware -* More work but also more control -* Show how to add webpack-dev-middleware and its entry point +```js +var Something = React.createClass({ + ... +}; -**XXX cleanup, details** +if (module.makeHot) { // Won't be true in production + Something = module.makeHot(Something); +} +``` -**Browserify** +Explicit API can also be used inside functions: -If you have this setup working, please consider submitting instructions as a PR. +```js +function generateClass(param) { + var Class = return React.createClass({ + ... + }; -**Meteor** + if (module.makeHot) { + Class = module.makeHot(Class, param); + } -* If you're using [webpack:webpack](https://atmospherejs.com/webpack/webpack) you can follow the webpack instructions or ask for help in [this](https://forums.meteor.com/t/use-webpack-with-meteor-simply-by-adding-packages-meteor-webpack-1-0-is-out/18819) forum post. + return Class; +} -* Otherwise, for HMR in "native" Meteor, type: `meteor remove ecmascript && meteor add gadicc:ecmascript-hot` or see the [README](https://github.com/gadicc/meteor-react-hotloader#readme) for more details. There are also some Meteor-specific RHLv3 install instructions [here](https://github.com/gadicc/meteor-react-hotloader/blob/master/docs/React_Hotloading.md). +``` -### Step 2/3: Using HMR to replace the root component +Note the second parameter: `makeHot` needs some way to distinguish components of same type inside on module. By default, it uses `displayName` of given component class, but in case of dynamically generated classes (or if you're not using JSX), you have to provide it yourself. -When the HMR runtime receives an updated module, it first checks to see if the module knows how to update itself, and then ascends the import/require chain looking for a parent module that can accept the update. We want our root component to be able to accept an update from any child component. +### Manual mode (experimental) -If your client entry point looks like this: +You can now use `react-hot?manual` instead of `react-hot` in Webpack config to turn on manual mode. In manual mode, “accepting” hot updates is up to you; modules won't accept themselves automatically. This can be used, for example, to put reloading logic on very top of the application and [hot-reload routes as well as components](https://github.com/rackt/react-router/pull/606#issuecomment-66936975). It will also work better when you have a lot of modules that export component-generating functions because updates will propagate to the top. (Don't worry if you don't understand this; it's just something experimental you might want to try to integrate hot reloading deeper into your app.) -```jsx -import React from 'react'; -import { render } from 'react-dom'; -import RootContainer from './containers/rootContainer.js'; +### Usage with external React -render(, document.elementById('react-root')); -``` -you would add the following code to accept changes to RootContainer _or any of it's descendants_. - -```jsx - if (module.hot) { - module.hot.accept('./containers/rootContainer.js', () => { - const NextRootContainer = require('./containers/rootContainer.js').default; - render(, document.elementById('react-root')); - } - } -``` -Note, with no further steps, this enough to hotload changes to React components, but state will not be preserved. If you externalize all your state in a state store like Redux, this might be enough. +If you're using external standalone React bundle instead of NPM package, Hot Loader will fail because it relies on `react/lib/ReactMount` which is not exposed in precompiled React. It needs `ReactMount` to keep track of mounted React component instances on the page. However, you can supply your own root instance provider: -### Step 3/3: Adding React Hot Loader to preserve state +```js +// Your app's index.js -The final step adds adds `react-hot-loader` to our project to preserve _component state_ across hot loads. +var React = require('react'), + router = require('./router'); -1. Install the package: +var rootInstance = null; - ```sh - $ npm install --save-dev react-hot-loader - ``` -1. Add the package to your config. - - a. If you use Babel, modify your `.babelrc` to ensure it includes at least: - - ```js - { - "plugins": [ "react-hot-loader/babel" ] - } - ``` - b. Alternatively, in Webpack, add `react-hot-loader/webpack` to your loaders - - ```js - // webpackConfig.js - - // TODO: Would love some help showing the shape of the webpack config without - // overwhelming users either - just want it to be familiar enough. I suppose we could - // also declare a variable and assign the require statement to it? (Just an idea) - devtool: ..., - entry: [...], - module: { - loaders: [{ - test: /\.js$/, - loaders: ['react-hot-loader/webpack', 'babel'], - include: path.join(__dirname, 'src') - }] - } - - ``` - -1. Add following line to the top of your main entry point: - ```js - import 'react-hot-loader/patch'; - ``` - -1. Wrap your `` inside of an ``: - - ```jsx - import { AppContainer } from 'react-hot-loader'; - import RootContainer from './containers/rootContainer.js'; - - render(( - - - - ), document.getElementById('react-root')); - ``` - **XXX pending [gaearon/react hot loader#244](https://github.com/gaearon/react-hot-loader/issues/244)** - - You should do this for both instances, e.g. your original mount and your mount code inside of the `module.hot.accept()` function. `` must wrap only a single, React component. - -That's it! - -## Putting it all together - -If you've gotten this far - you're almost done! But before showing you what your app's -main entry point might look like, let's clarify a few things. - -`AppContainer` -> `AppContainer` is a component provided by *this* library (`react-hot-loader`), it serves to -wrap your entire app in order to provide hot reloading goodness! - -`RootContainer` -> On the other hand, `RootContainer` represents any application's top-level component, prior -to implementing the `AppContainer` mentioned above. Keep in mind that this can be substituted -for an existing wrapper/parent component. - -Your application's main entry point might look like the code presented below. Notice that -we are targeting and subsequently rendering into a particular DOM element's id (conveniently named `react-root`). - -```jsx -import 'react-hot-loader/patch'; -import React from 'react'; -import { render } from 'react-dom'; -// See notes above re: AppContainer and RootContainer -import { AppContainer } from 'react-hot-loader' -import RootContainer from './containers/rootContainer.js'; - -render(( - - - -), document.getElementById('react-root')); +router.run(function (Handler, state) { + rootInstance = React.render(, document.body); +}); if (module.hot) { - module.hot.accept('./containers/rootContainer.js', () => { - const NextRootContainer = require('./containers/rootContainer.js'); - - render(( - - - - ), document.getElementById('react-root')); - }) + require('react-hot-loader/Injection').RootInstanceProvider.injectProvider({ + getRootInstances: function () { + // Help React Hot Loader figure out the root component instances on the page: + return [rootInstance]; + } + }); } ``` -### Checking that everything is working properly - -**XXX** - -## Troubleshooting - -**XXX could/should be in another file** +You'll only need this if you [use a precompiled version of React](https://github.com/gaearon/react-hot-loader/issues/53). If you use React NPM package, this is not necessary. You should generally use React NPM package unless you have good reason not to. -## Tips and Tricks +### Source Maps -**XXX could/should be in another file** +If you use `devtool: 'source-map'` (or its equivalent), source maps will be emitted to hide hot reloading code. -**How to get an error in your console too:** +Source maps slow down your project. Use `devtool: 'eval'` for best build performance. -The `Redbox` errors are great to clearly see rendering issues, and avoiding an uncaught error from breaking your app. -But there are some advantages to a thrown error in the console too, like filename resolution via sourcemaps, and click-to-open. -To get the best of both worlds, modify your app entry point as follows: - -```jsx -import Redbox from 'redbox-react'; - -const consoleErrorReporter = ({error}) => { - // We throw in a different context, so the app still doesn't break! - setTimeout(() => { throw error; }); - return ; -}; - -consoleErrorReporter.propTypes = { - error: React.PropTypes.error -}; - -render(( - - - -), document.getElementById('react-root')); -``` +Hot reloading code is just one line in the beginning and one line in the end of each module so you might not need source maps at all. -## Where to ask for Help +### React Hot API -**XXX** +If you're authoring a build tool, you might be interested to hear that React Hot Loader brains have been extracted into runtime-agnostic [React Hot API](https://github.com/gaearon/react-hot-api). React Hot Loader just binds that API to Webpack runtime, but you can implement yours too. From ee4f18d3cfb13f663acdef38a36f0a9dffd353e6 Mon Sep 17 00:00:00 2001 From: Gadi Cohen Date: Tue, 26 Apr 2016 15:28:44 +0200 Subject: [PATCH 12/20] Add Tips & Tricks doc --- docs/TipsAndTricks.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 docs/TipsAndTricks.md diff --git a/docs/TipsAndTricks.md b/docs/TipsAndTricks.md new file mode 100644 index 000000000..a47e9eff7 --- /dev/null +++ b/docs/TipsAndTricks.md @@ -0,0 +1,24 @@ +# Tips and Tricks + +**How to get an error in your console too:** + +The `Redbox` errors are great to clearly see rendering issues, and avoiding an uncaught error from breaking your app. But there are some advantages to a thrown error in the console too, like filename resolution via sourcemaps, and click-to-open. To get the best of both worlds, modify your app entry point as follows: + +```jsx +import Redbox from 'redbox-react'; + +const consoleErrorReporter = ({error}) => { + console.error(error); + return ; +}; + +consoleErrorReporter.propTypes = { + error: React.PropTypes.error +}; + +render(( + + + +), document.getElementById('react-root')); +``` \ No newline at end of file From 6f2c583b56c72250fc5283df76b1b235fe926138 Mon Sep 17 00:00:00 2001 From: Gadi Cohen Date: Tue, 3 May 2016 14:58:37 +0200 Subject: [PATCH 13/20] consoleErrorReporter: correct error proptype & npm i redbox-react --- docs/TipsAndTricks.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/TipsAndTricks.md b/docs/TipsAndTricks.md index a47e9eff7..7a8f21a6b 100644 --- a/docs/TipsAndTricks.md +++ b/docs/TipsAndTricks.md @@ -13,7 +13,7 @@ const consoleErrorReporter = ({error}) => { }; consoleErrorReporter.propTypes = { - error: React.PropTypes.error + error: React.PropTypes.instanceOf(Error).isRequired }; render(( @@ -21,4 +21,6 @@ render(( ), document.getElementById('react-root')); -``` \ No newline at end of file +``` + +You'll also need to `npm install --save-dev redbox-react`. From 26f4c14abb6e6eb91c99e3e9a4cf0fbfd8b6b278 Mon Sep 17 00:00:00 2001 From: jameskraus Date: Tue, 23 Aug 2016 15:18:57 -0400 Subject: [PATCH 14/20] Fixing the broken V3 example in the docs --- docs/README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/README.md b/docs/README.md index a3abe338a..a0df195e3 100644 --- a/docs/README.md +++ b/docs/README.md @@ -50,11 +50,12 @@ ReactDOM.render( // Hot Module Replacement API if (module.hot) { module.hot.accept('./containers/App', () => { - render( + const NextApp = require('./containers/App').default; + ReactDOM.render( - + - />, + , document.getElementById('root') ); }); From 574a4107304f6b01ce5140536e58422f6632fa96 Mon Sep 17 00:00:00 2001 From: Cale Newman Date: Tue, 18 Oct 2016 19:41:59 -0500 Subject: [PATCH 15/20] Add "Known Limitations" doc This shows some common problems with using RHL 3, along with workarounds (since they don't currently have great solutions) --- docs/Known Limitations.md | 55 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 docs/Known Limitations.md diff --git a/docs/Known Limitations.md b/docs/Known Limitations.md new file mode 100644 index 000000000..ffdf08c31 --- /dev/null +++ b/docs/Known Limitations.md @@ -0,0 +1,55 @@ +## Known Limitations + +### require.ensure +If you want to use Webpack code splitting via `require.ensure`, you'll need to add an additional `module.hot.accept` callback within the `require.ensure` block, like this: + +```js +require.ensure([], (require) => { + if (module.hot) { + module.hot.accept('../components/App', () => { + loadComponent(require('../components/App').default); + }) + } + loadComponent(require('../components/App').default); +}); +``` + +Note that if you're using React Router (pre-4.0), this will only work with `getChildRoutes`, but not `getComponent`, since `getComponent`'s callback will only load a component once. + +### Checking Element `type`s +Because React Hot Loader creates proxied versions of your components, comparing reference types of elements won't work: + +```js +const element = ; +console.log(element.type === Component); // false +``` + +One workaround is to create an element (that will have the `type` of the proxied component): + +```js +const ComponentType = ().type; +const element = ; +console.log(element.type === ComponentType); // true +``` + +### Reassigning Components +React Hot Loader will only try to reload the original component reference, so if you reassign it to another variable like this: + +```js +let App = () => (
hello
); +App = connect()(App); +export default App; +``` + +RHL won't reload it. Instead, you'll need to define it once: + +```js +const App = () => (
hello
); +export default connect()(App); +``` + +### Using Non-Transformed Classes +Unfortunately, right now classes need to be compiled by either Babel or TypeScript to the ES5 equivalent (see [#313](https://github.com/gaearon/react-hot-loader/issues/313)). + +### Decorators +Components that are decorated (using something like [`@autobind`](https://github.com/andreypopp/autobind-decorator)) currently do not retain state when being hot-reloaded. (see [#279](https://github.com/gaearon/react-hot-loader/issues/279)) From 142201d0fdc56dc99604855f4c8289f3ec24a033 Mon Sep 17 00:00:00 2001 From: Cale Newman Date: Tue, 18 Oct 2016 21:56:50 -0500 Subject: [PATCH 16/20] Add System.import section on known limitations Also, remove the section about transforming ES2015 classes because we want to fix that soon. --- docs/Known Limitations.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/docs/Known Limitations.md b/docs/Known Limitations.md index ffdf08c31..e2c5c943b 100644 --- a/docs/Known Limitations.md +++ b/docs/Known Limitations.md @@ -1,6 +1,6 @@ ## Known Limitations -### require.ensure +### Code Splitting If you want to use Webpack code splitting via `require.ensure`, you'll need to add an additional `module.hot.accept` callback within the `require.ensure` block, like this: ```js @@ -16,6 +16,8 @@ require.ensure([], (require) => { Note that if you're using React Router (pre-4.0), this will only work with `getChildRoutes`, but not `getComponent`, since `getComponent`'s callback will only load a component once. +Also, if you're using the Webpack 2 beta, you can use `System.import` without extra `module.hot.accept` calls, although there are still a [few issues with it](https://github.com/gaearon/react-hot-loader/issues/303). + ### Checking Element `type`s Because React Hot Loader creates proxied versions of your components, comparing reference types of elements won't work: @@ -41,15 +43,12 @@ App = connect()(App); export default App; ``` -RHL won't reload it. Instead, you'll need to define it once: +React Hot Loader won't reload it. Instead, you'll need to define it once: ```js const App = () => (
hello
); export default connect()(App); ``` -### Using Non-Transformed Classes -Unfortunately, right now classes need to be compiled by either Babel or TypeScript to the ES5 equivalent (see [#313](https://github.com/gaearon/react-hot-loader/issues/313)). - ### Decorators Components that are decorated (using something like [`@autobind`](https://github.com/andreypopp/autobind-decorator)) currently do not retain state when being hot-reloaded. (see [#279](https://github.com/gaearon/react-hot-loader/issues/279)) From 9da8e84bdef6ee8b67e409063ec0c9a5b013ebbc Mon Sep 17 00:00:00 2001 From: Cale Newman Date: Sun, 30 Oct 2016 19:44:04 -0500 Subject: [PATCH 17/20] document migrating from create-react-app --- docs/README.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/docs/README.md b/docs/README.md index a0df195e3..9cfb8c594 100644 --- a/docs/README.md +++ b/docs/README.md @@ -64,6 +64,40 @@ if (module.hot) { You can also check out [this commit for the migration of a TodoMVC app from 1.0 to 3.0.](https://github.com/gaearon/redux-devtools/commit/64f58b7010a1b2a71ad16716eb37ac1031f93915) +## Migrating from [create-react-app](https://github.com/facebookincubator/create-react-app) + +* Run `npm run eject` +* Install React Hot Loader (`npm install --save-dev react-hot-loader@3.0.0-beta.6`) +* In `config/webpack.config.dev.js`: + 1. Add `'react-hot-loader/patch'` to entry array (anywhere before `paths.appIndexJs`). It should now look like (excluding comments): + ```js + entry: [ + 'react-hot-loader/patch', + require.resolve('react-dev-utils/webpackHotDevClient'), + require.resolve('./polyfills'), + paths.appIndexJs + ] + ``` + + 2. Add `'react-hot-loader/babel'` to Babel loader configuration. The loader should now look like: + ```js + { + test: /\.(js|jsx)$/, + include: paths.appSrc, + loader: 'babel', + query: { + cacheDirectory: findCacheDir({ + name: 'react-scripts' + }), + plugins: [ + 'react-hot-loader/babel' + ] + } + } + ``` + +* Add `AppContainer` to `src/index.js` (see `AppContainer` section in [Migration to 3.0 above](https://github.com/gaearon/react-hot-loader/blob/next-docs/docs/README.md#migration-to-30)) + ### Source Maps If you use `devtool: 'source-map'` (or its equivalent), source maps will be emitted to hide hot reloading code. From d2706c69c7e9baca69be497b3c2d33ca8c91dfbe Mon Sep 17 00:00:00 2001 From: Cale Newman Date: Wed, 2 Nov 2016 11:25:32 -0500 Subject: [PATCH 18/20] Update README.md --- docs/README.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/docs/README.md b/docs/README.md index 9cfb8c594..d041fa5eb 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,5 +1,5 @@ ### Starter Kit -* [react-hot-boilerplate](https://github.com/gaearon/react-hot-boilerplate) (Bare minimum) +* [react-hot-boilerplate](https://github.com/gaearon/react-hot-boilerplate/tree/next) (Bare minimum) ### Migration to 3.0 - If you're using Babel and ES6, remove the `react-hot` loader from any loaders in your Webpack config, and add `react-hot-loader/babel` to the `plugins` section of your `.babelrc`: @@ -30,11 +30,11 @@ - 'react-hot-loader/patch' should be placed at the top of the `entry` section in your Webpack config. An error will occur if any code runs before `react-hot-loader/patch` has, so put it in the first position. -- `` - AppContainer is a component that handles module reloading, as well as error handling. The root component of your app should be nested in AppContainer as a child. When in production, AppContainer is automatically disabled, and simply returns its children. +- `` is a component that handles module reloading, as well as error handling. The root component of your app should be nested in AppContainer as a child. When in production, AppContainer is automatically disabled, and simply returns its children. - React Hot Loader 3 does not hide the hot module replacement API, so the following needs to be added below wherever you call `ReactDOM.render` in your app: -```js +```jsx import React from 'react' import ReactDOM from 'react-dom' import { AppContainer } from 'react-hot-loader' @@ -54,8 +54,7 @@ if (module.hot) { ReactDOM.render( - - , + , document.getElementById('root') ); }); From 61582ed4f771048c3eaafcc589b196773168a99e Mon Sep 17 00:00:00 2001 From: Cale Newman Date: Sat, 5 Nov 2016 16:25:08 -0500 Subject: [PATCH 19/20] update Known Limitations doc add JSX syntax highlighting and add another workaround for comparing React element types. --- docs/Known Limitations.md | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/docs/Known Limitations.md b/docs/Known Limitations.md index e2c5c943b..13ba477d8 100644 --- a/docs/Known Limitations.md +++ b/docs/Known Limitations.md @@ -21,23 +21,31 @@ Also, if you're using the Webpack 2 beta, you can use `System.import` without ex ### Checking Element `type`s Because React Hot Loader creates proxied versions of your components, comparing reference types of elements won't work: -```js +```jsx const element = ; console.log(element.type === Component); // false ``` One workaround is to create an element (that will have the `type` of the proxied component): -```js +```jsx const ComponentType = ().type; const element = ; console.log(element.type === ComponentType); // true ``` +You can also set a property on the component class: + +```jsx +const Widget = () =>
hi
; +Widget.isWidgetType = true; +console.log(.type.isWidgetType); // true +``` + ### Reassigning Components React Hot Loader will only try to reload the original component reference, so if you reassign it to another variable like this: -```js +```jsx let App = () => (
hello
); App = connect()(App); export default App; @@ -45,7 +53,7 @@ export default App; React Hot Loader won't reload it. Instead, you'll need to define it once: -```js +```jsx const App = () => (
hello
); export default connect()(App); ``` From 8365f09f7446c9607a51ae35ab781880ac19fc59 Mon Sep 17 00:00:00 2001 From: Cale Newman Date: Sat, 12 Nov 2016 15:45:08 -0600 Subject: [PATCH 20/20] Update README.md --- docs/README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/docs/README.md b/docs/README.md index d041fa5eb..7d6b953e9 100644 --- a/docs/README.md +++ b/docs/README.md @@ -97,6 +97,25 @@ You can also check out [this commit for the migration of a TodoMVC app from 1.0 * Add `AppContainer` to `src/index.js` (see `AppContainer` section in [Migration to 3.0 above](https://github.com/gaearon/react-hot-loader/blob/next-docs/docs/README.md#migration-to-30)) +## Webpack 2 + +Because Webpack 2 has built-in support for ES2015 modules, you won't need to re-require your app root in `module.hot.accept`. The example above becomes: + +```jsx +if (module.hot) { + module.hot.accept('./containers/App', () => { + ReactDOM.render( + + + , + document.getElementById('root') + ); + }); +} +``` + +To make this work, you'll need to opt out of Babel transpiling ES2015 modules by changing the Babel ES2015 preset to be `["es2015", { "modules": false }]` + ### Source Maps If you use `devtool: 'source-map'` (or its equivalent), source maps will be emitted to hide hot reloading code.