Skip to content

Commit

Permalink
merge with the 3-step getting started doc mistakenly in #257
Browse files Browse the repository at this point in the history
  • Loading branch information
gadicc committed Apr 26, 2016
1 parent f8a8820 commit 48470a5
Showing 1 changed file with 155 additions and 121 deletions.
276 changes: 155 additions & 121 deletions getstarted.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,171 +3,212 @@ layout: page
title: Get Started
---

React Hot Loader is a plugin for Webpack that allows instantaneous live refresh without losing state while editing React components.
React Hot Loader is a plugin that allows instantaneous live refresh without losing state while editing React components.

If you use Browserify, RequireJS or another JavaScript bundler, you need to switch to Webpack first. Webpack supports all popular module formats. If you know a good guide on migrating to Webpack, <a href="https://github.com/gaearon/react-hot-loader/issues/new" target="_blank">let me know</a> and Iʼll link to it.
It works with Webpack and other bundlers that support both Hot Module Replacement (HMR) and Babel plugins.

## 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/)

## 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:

#### Webpack

* 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

**XXX cleanup, details**

This tutorial assumes that you already have a working Webpack configuration and `WebpackDevServer` compiles and serves your code. If youʼd rather play with a ready-made example, try <a href="https://github.com/gaearon/react-hot-boilerplate" target="_blank">react-hot-boilerplate</a>.

## Installation
**Development Server**

The only package that you need to install is `react-hot-loader`. Do that by running:
With the loader installed, it is now time to configure a small dev server for Webpack to use. The key aspect of this configuration is that when creating a `new WebpackDevServer`, you need to specify `hot: true` as an option. For example, you can add an entirely new file called `server.js` and simply include the <a href="https://github.com/gaearon/react-hot-boilerplate/blob/master/server.js" target="_blank">server provided in the boilerplate</a>.

{% highlight sh %}
npm install --save-dev react-hot-loader
If you like, you may edit <a href="https://github.com/gaearon/react-hot-boilerplate/blob/master/package.json" target='_blank'>`package.json`</a> to call the Webpack server on `npm start`:

{% highlight js %}
"scripts": {
"start": "node server.js"
},
{% endhighlight %}

## Setup
**Configuration**

### Step 1. Setup Webpack Config
The first thing you'll need to do is create a Webpack config for development. This config must be separate from the one you use for production.
It is time to configure Webpack itself.
In your <a href="https://github.com/gaearon/react-hot-boilerplate/blob/master/webpack.config.js" target="_blank">`webpack.config.js`</a>, configure the `entry` to include the dev server and the hot reloading server. Put them in array before your appʼs entry point:

Here is a basic example:
```
webpack.config.dev.js
```
{% highlight js %}
var path = require('path');
var webpack = require('webpack');

module.exports = {
devtool: 'eval',
entry: [
'./src/index'
],
output: {
path: path.join(__dirname, 'dist'),
filename: 'bundle.js',
publicPath: '/static/'
},
plugins: [
],
module: {
loaders: [{
test: /\.js$/,
loaders: ['babel'],
include: path.join(__dirname, 'src')
}]
}
};
entry: [
'webpack-dev-server/client?http://0.0.0.0:3000', // WebpackDevServer host and port
'webpack/hot/only-dev-server', // "only" prevents reload on syntax errors
'./scripts/index' // Your appʼs entry point
]
{% endhighlight %}

### Step 2. Add HotModuleReplacementPlugin
Now that we have a development config, we need to add Webpack's `HotModuleReplacementPlugin`. This can be done by adding `new webpack.HotModuleReplacementPlugin()` to the `plugins` section:
Finally, the Hot Replacement plugin from Webpack has to be included in the `plugins` section of the config. If you have not used Webpack plugins before, donʼt forget to add `var webpack = require('webpack');` at the top of your config. Then just add `new webpack.HotModuleReplacementPlugin()` to the `plugins` section:

{% highlight js %}
plugins: [
new webpack.HotModuleReplacementPlugin()
]
{% endhighlight %}

>Note: If you are using Webpack Dev Server command line interface instead of its Node API, and you specify `--hot` mode, *don't* add this plugin. It is mutually exclusive with the `--hot` option.
### Step 3. Setup Development Server
If you are only rendering on the client, consider using `WebpackDevServer` as your development server. It is easier to setup than `webpack-dev-middleware`.
#### Browserify

**WebpackDevServer**
Install `webpack-dev-server` via npm:
```
npm install webpack-dev-server
```
If you have this setup working, please consider submitting instructions as a PR.

Inside your Webpack development config,
#### Meteor

If you are going to be using server-side rendering, consider using Express + `webpack-dev-middleware`. It is more work to set up than `WebpackDevServer`, but you will have more control.
* 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.

**webpack-dev-middleware**
* Otherwise, for HMR in "native" Meteor, type: `meteor remove ecmascript && meteor add gadicc:ecmascript-hot` or see the [README](https://github.com/gadicc/meteor-hmr#readme) for more details. There are also some Meteor-specific RHLv3 install instructions [here](https://github.com/gadicc/meteor-hmr/blob/master/docs/React_Hotloading.md).

### Step 4. Use HMR to Replace Root Component
Inside `index.js`, or wherever you are rendering your component, you need to add the following to re-render the root component:
### Step 2/3: Using HMR to replace the root component

{% highlight js %}
if (module.hot) {
module.hot.accept('./App', () => {
ReactDOM.render(
<AppContainer>
<App/>
</AppContainer>,
rootEl
);
});
{% endhighlight %}
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.

### Step 5. Add React Hot Loader To Preserve State
Now it's time to add React Hot Loader using either the Babel or Webpack plugin.
If your client entry point looks like this:

- If you use Babel:
- Add `react-hot-loader/babel` to `plugins` inside of `.babelrc`.
```js
import React from 'react';
import { render } from 'react-dom';
import RootContainer from './containers/rootContainer.js';

{% highlight js %}
{
"presets": ["es2015", "stage-0", "react"],
"plugins": ["react-hot-loader/babel"]
}
{% endhighlight %}
render(<RootContainer />, document.elementById('react-root'));
```
you would add the following code to accept changes to RootContainer _or any of it's descendants_.

```js
if (module.hot) {
module.hot.accept('./containers/rootContainer.js', () => {
const NextRootContainer = require('./containers/rootContainer.js').default;
render(<NextRootContainer />, 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 don't use Babel:
- Add `react-hot-loader/webpack` to `loaders` in your Webpack config.
#### Step 3/3: Adding React Hot Loader to preserve state
{% highlight js %}
The final step adds adds `react-hot-loader` to our project to preserve _component state_ across hot loads.
{% endhighlight %}
1. Install the package:
```sh
$ npm install --save-dev react-hot-loader
```
1. Add the package to your config.
- Add `react-hot-loader/patch` as the first entry point in your Webpack config.
a. If you use Babel, modify your `.babelrc` to ensure it includes at least:
### Step 6. AppContainer
The final step is to
```js
{
"plugins": [ "react-hot-loader/babel" ]
}
```
b. Alternatively, in Webpack, add `react-hot-loader/webpack` to your loaders
With the loader installed, it is now time to configure a small dev server for Webpack to use. The key aspect of this configuration is that when creating a `new WebpackDevServer`, you need to specify `hot: true` as an option. For example, you can add an entirely new file called `server.js` and simply include the <a href="https://github.com/gaearon/react-hot-boilerplate/blob/master/server.js" target="_blank">server provided in the boilerplate</a>.
```js
// webpackConfig.js

If you like, you may edit <a href="https://github.com/gaearon/react-hot-boilerplate/blob/master/package.json" target='_blank'>`package.json`</a> to call the Webpack server on `npm start`:
// 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')
}]
}

{% highlight js %}
"scripts": {
"start": "node server.js"
},
{% endhighlight %}
```
### Configuration
1. Add following line to the top of your main entry point:
It is time to configure Webpack itself.
In your <a href="https://github.com/gaearon/react-hot-boilerplate/blob/master/webpack.config.js" target="_blank">`webpack.config.js`</a>, configure the `entry` to include the dev server and the hot reloading server. Put them in array before your appʼs entry point:
```js
import 'react-hot-loader/patch';
```
{% highlight js %}
entry: [
'webpack-dev-server/client?http://0.0.0.0:3000', // WebpackDevServer host and port
'webpack/hot/only-dev-server', // "only" prevents reload on syntax errors
'./scripts/index' // Your appʼs entry point
]
{% endhighlight %}
1. Wrap your `<RootContainer/>` inside of an `<AppContainer>`:
Next we need to tell Webpack to use React Hot Loader for the components. If you configured Webpack for React, you may already use `babel-loader` (ex `6to5-loader`) or `jsx-loader` for JS(X) files. Find that line in <a href="https://github.com/gaearon/react-hot-boilerplate/blob/master/webpack.config.js">`webpack.config.js`</a> and put `react-hot` before other loader(s).
```js
import { AppContainer } from 'react-hot-loader';
import RootContainer from './containers/rootContainer.js';

If you only had one loader before, be sure to change `loader` to `loaders` so it takes array as an input:
render((
<AppContainer>
<RootContainer />
</AppContainer>
), document.getElementById('react-root'));
```
**XXX pending [gaearon/react hot loader#244](https://github.com/gaearon/react-hot-loader/issues/244)**
{% highlight js %}
module: {
loaders: [
{ test: /\.jsx?$/, loaders: ['react-hot', 'jsx?harmony'], include: path.join(__dirname, 'src') }
]
}
{% endhighlight %}
You should do this for both instances, e.g. your original mount and your mount code inside of the `module.hot.accept()` function. `<AppContainer>` must wrap only a single, React component.
If you donʼt use JSX, itʼs fine. Just make sure your components are all transformed with `react-hot`.
That's it!
Finally, the Hot Replacement plugin from Webpack has to be included in the `plugins` section of the config. If you have not used Webpack plugins before, donʼt forget to add `var webpack = require('webpack');` at the top of your config. Then just add `new webpack.HotModuleReplacementPlugin()` to the `plugins` section:
### Putting it all together
{% highlight js %}
plugins: [
new webpack.HotModuleReplacementPlugin()
]
{% endhighlight %}
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.
>Note: If you are using Webpack Dev Server command line interface instead of its Node API, and you specify `--hot` mode, *don't* add this plugin. It is mutually exclusive with the `--hot` option.
`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!
### Usage
`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.
Start the server we configured earlier via `npm start` and open the dev server URL in browser. To test hot reloading, just edit any component and watch the changes happen live!
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`).
```js
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((
<AppContainer>
<RootContainer />
</AppContainer>
), document.getElementById('react-root'));

if (module.hot) {
module.hot.accept('./containers/rootContainer.js', () => {
const NextRootContainer = require('./containers/rootContainer.js');

render((
<AppContainer>
<NextRootContainer />
</AppContainer>
), document.getElementById('react-root'));
})
}
```
### Troubleshooting
Expand All @@ -176,10 +217,3 @@ If hot reloading doesnʼt work, itʼs usually due to a deviation from the config
If youʼre stuck, <a href="https://github.com/gaearon/react-hot-loader/issues/new" target="_blank">file an issue</a> or ask for help in <a href="https://gitter.im/gaearon/react-hot-loader" target="_blank">the Gitter room</a>, and weʼll try to figure it out.
Happy hot reloading!

<br>
<small>
This guide was originally written as a blog post by [Joseph Furlott](http://jmfurlott.com/setting-up-react-hot-loader/).
<br>
You can find a more technical explanation in an [older introductory post](/react-hot-loader/2014/07/23/integrating-jsx-live-reload-into-your-react-workflow/).
</small>

0 comments on commit 48470a5

Please sign in to comment.