Several years ago, it was becoming tiring (and annoying in the case where the changes do not render as expected) to refresh the browser to see the most recent updates on a web project.
In a bid to solve this problem, developers created several plugins and tools that can cause the DOM
to re-render on each save without even doing a full-reload.
This is called hot reloading. It works by replacing a module of the application during runtime with an updated one so that it’s available for instant use.
This leads us to talk about Hot Module Replacement (HMR).
HMR, as the name implies, can replace modules without restarting the server and can easily be enabled with different bundlers.
On webpack’s website, it says:
Hot Module Replacement (HMR) exchanges, adds, or removes modules while an application is running, without a full reload. This can significantly speed up development in a few ways.
HMR solves the problem of hot reloading, makes it easier to tweak styling (almost comparable to changing styles in the browser’s debugger) and generally speeds up the development process.
But what happens when we live reload in a component based framework like React that keeps track of its states? We lose the state.
The reason we lose the state can be understood by first understanding the difference between hot reload and live reload:
The primary aim of this tutorial is to demonstrate how we can quickly setup react-hot-loader for our React application so that its components can be live reloaded without a loss in state.
The resulting code for this article is available here on GitHub.
If you don’t already have the create-react-app
tool on your computer, you can install it with this command:
npm install -g create-react-app
Let’s spin up a new instance of a create-react-app project and navigate into the newly created directory with these commands:
create-react-app react-hot-loader-demo cd react-hot-loader-demo
We can run the application using this command:
npm start
This command starts the web server that points to the address localhost://3000
. We will visit this address on our browser and keep the tab open because throughout this tutorial, we will be monitoring the updates and behavior of the application in the browser.
When we create a new project using the create-react-app
, the HMR plugin is enabled by default in the webpack
configuration file, though we can’t see this file because create-react-app
configures our application for us and hides the configuration files.
We can see the HMR’s hot reloading in action by adding a basic style to the index.css
file that’s included out of the box. We’ll add a property value pair — background: lightblue
— to the body style:
body { margin: 0; padding: 0; font-family: sans-serif; background: lightblue; }
On saving this, the application updates without the browser tab refreshing.
This behavior is possible because the HMR is able to replace a module of code in the application during runtime.
Now let’s see what happens when we edit the content in the App.js
file, we can do something as basic as replacing the “To get started, edit src/App.js
and save to reload” with “This is a tutorial to setup React Hot Loader.”
import React, { Component } from 'react'; import logo from './logo.svg'; import './App.css'; class App extends Component { render() { return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <h1 className="App-title">Welcome to React</h1> </header> <p className="App-intro"> This is a tutorial to setup React Hotloader.. </p> </div> ); } } export default App;
No Title
No Description
This time, it can be observed that the application does a live reload and the entire page refreshes so that the change can be implemented. This happens because the HMR is unable to decide how to handle the update to this JavaScript file.
Naturally, hot reloads are possible when the HMR receives an update at runtime and confirms that the concerned module knows how to update itself. Where the module knows how to update itself, the HMR then goes up the import/require chain, searching for a parent module that can accept the update.
Let’s tweak our application a bit so that the HMR knows how to handle JavaScript files for hot reloads.
In the index.js
file (which is the entry point of our application), we will add this line of code:
if(module.hot){ module.hot.accept() }
This code will make it possible for the updates to our React components to do hot reloads because the parent module (index.js
) now knows how to accept and process the runtime updates.
Now, lets make a quick edit to the code and save it to see if the state persists, we will replace Will the state be preserved? The value is {this.state.number}
with “Does the state persist now? The value is {this.state.number}”
:
import React, { Component } from 'react'; import logo from './logo.svg'; import './App.css'; class App extends Component { render() { return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <h1 className="App-title">Welcome to React</h1> </header> <p className="App-intro"> This update will not cause a refresh to the browser tab. </p> </div> ); } } export default App;
The resulting page:
No Title
No Description
This update is handled as a hot reload and didn’t cause the browser to refresh!
Everything we have done has been awesome so far. Let’s see what happens when we include a basic state variable to the application, we can also add a function to make it increment on click events:
import React, { Component } from 'react'; import logo from './logo.svg'; import './App.css'; class App extends Component { constructor(props){ super(props); this.state = { number : 0 } } render() { return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <h1 className="App-title">Welcome to React</h1> </header> <p className="App-intro"> The current value of number is {this.state.number} </p> <button onClick={()=>this.setState({number : this.state.number + 1})}>+</button> </div> ); } } export default App;
When we save this code and it hot reloads, we can click on the buttons 12 times to increment it:
No Title
No Description
It works just as we wanted it to, however, what happens if we make an edit to the JavaScript file and save it now? It should do a hot reload just as before and also preserve the value of the state variable, right? Let’s see about that. Let’s replace the line The current value of number is {this.state.number}
with Will the state be preserved? The value is {this.state.number}
:
import React, { Component } from 'react'; import logo from './logo.svg'; import './App.css'; class App extends Component { constructor(props){ super(props); this.state = { number : 0 } } render() { return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <h1 className="App-title">Welcome to React</h1> </header> <p className="App-intro"> Will the state be preserved? The value is {this.state.number} </p> <button onClick={()=>this.setState({number : this.state.number + 1})}>+</button> </div> ); } } export default App;
On saving it, we get this screen:
No Title
No Description
What just happened? Wasn’t our number equals to twelve a few seconds ago? Where did that value go? That’s right, during the hot reload, the component unmounted and the constructor had to re-run when the component was mounted again. We lose state data even with the hot reload features HMR gives to us.
We can handle this issue using the React Hot Loader plugin and doing some configurations.
First of all, we want to eject our application so we can customize its configuration:
npm run eject
Running
npm run eject
copies all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc.) right into your project so you have full control over them. Commands likenpm start
andnpm run build
will still work, but they will point to the copied scripts so you can tweak them.Doing this is also permanent.
Next, we pull in React Hot Loader, which is a plugin that allows React components to be live reloaded without the loss of state. It works with webpack and other bundlers that support both HMR and Babel plugins.
npm install --save react-hot-loader
Linux users might need to prefix this command with
sudo
for permission reasons.
When this is done installing, we want to navigate into the config/webpack.config.dev.js
that is now available to us, add this line — plugins: ['react-hot-loader/babel']
— to the Babel loader configuration. This is what it should resemble:
// Process JS with Babel. { test: /\.(js|jsx)$/, include: paths.appSrc, loader: require.resolve('babel-loader'), options: { // This is a feature of `babel-loader` for Webpack (not Babel itself). // It enables caching results in ./node_modules/.cache/babel-loader/ // directory for faster rebuilds. cacheDirectory: true, plugins: ['react-hot-loader/babel'], }, }
The next thing we want do is open our application’s entry point again and wrap the app’s top-level component inside of an <AppContainer>
. We have to import import the <AppContainer>
from react-hot-loader
first:
import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './App'; import registerServiceWorker from './registerServiceWorker'; import { AppContainer } from 'react-hot-loader'; ReactDOM.render( <AppContainer> <App /> </AppContainer>, document.getElementById('root')); registerServiceWorker(); if(module.hot){ module.hot.accept() }
Let’s modify the module.hot
section so that we can also render the <AppContainer>
within it:
if(module.hot){ module.hot.accept('./App', () => { const NextApp = require('./App').default; ReactDOM.render( <AppContainer> <NextApp /> </AppContainer>, document.getElementById('root') ); }); }
This modification tells the <AppContainer>
to process the module reloading and handle any errors that might be created during runtime.
The would be disabled in the production environment.
Lastly, we need to require the react-hot-loader
patch into our entry point file:
require('react-hot-loader/patch');
We require this because it’s responsible for patching the code at low-level processing.
Let’s test our application to see if the state would be preserved on updates now, we can accomplish this by saving the updates we just made and running the application:
npm start
We can click on the button to increase the number variable to a number like 36:
No Title
No Description
Now, lets make a quick edit to the code and save it to see if the state persists:
import React, { Component } from 'react'; import logo from './logo.svg'; import './App.css'; class App extends Component { constructor(props){ super(props); this.state = { number : 0 } } render() { return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <h1 className="App-title">Welcome to React</h1> </header> <p className="App-intro"> Does the state persist now? The value is {this.state.number} </p> <button onClick={()=>this.setState({number : this.state.number + 1})}>+</button> </div> ); } } export default App;
This update causes the browser window to update to this:
No Title
No Description
Awesome! The state persisted despite the change we made to the document, this means that our react-hot-loader
works perfectly and handles module reloading for us.
In this tutorial, we have gone from understanding why hot loading is important to specify the difference between a hot reload and a live reload. We have also seen how to quickly and easily setup a React application for hot reload using the react-hot-loader
plugin so that the value of all states present in the application can be persisted regardless of the updates made. The final code of this article is available here on GitHub.
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to implement one-way and two-way data binding in Vue.js, using v-model and advanced techniques like defineModel for better apps.
Compare Prisma and Drizzle ORMs to learn their differences, strengths, and weaknesses for data access and migrations.
It’s easy for devs to default to JavaScript to fix every problem. Let’s use the RoLP to find simpler alternatives with HTML and CSS.
Learn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
3 Replies to "Set up React Hot Loader in 10 minutes"
Is there a way to have the browser hot reload the app only on saving it? Having it reload while i type is a bit annoying.. thanks!
Could it be that your editor is auto-saving?
Can u please help me setup react-hot-loader in webpack 5 without create-react-app. I tried but when the text is changed the browser is not updating when I click on button to change state it is updating the state and text. Please help !!