Blogged Answers: Webpack HMR vs React-Hot-Loader
This is a post in the Blogged Answers series.
I've frequently seen people get confused while trying to set up Webpack's Hot Module Replacement (HMR) capabilities for use in a React application. The most common source of confusion is the assumption that they must use the React-Hot-Loader tool to turn on HMR. That's not the case, and I'd like to clarify the differences.
Hot Module Replacement is a core capability offered by Webpack. Webpack's compiler offers a module.hot.accept()
API. Your application code can register callbacks to run when certain files have been recompiled. Here's how the process works:
- Your Webpack config needs to include the HMR client code as an entry point in addition to your actual application entry file. This adds a small piece to the client bundle, which will open a websocket connection back to the Webpack dev server.
- Your client application code calls
module.hot.accept("./someFileName", callbackToRunWhenThatFileIsRecompiled)
. - Webpack's dev server watches your files for changes, and recompiles source files when you hit save
- The dev server sends a message to the client, announcing that those files have been recompiled, and including the new versions of the code
- The client runs any
module.hot.accept
callbacks that match that file's path
What happens in that callback is up to you, but normal usage is to re-import the changed file and swap out the affected code, such as a React component or a Redux reducer. If you use this "plain HMR" approach in your own application, you would generally only have one or two calls to module.hot.accept()
, such as one for your root component file and one for your root Reducer file. I show an example of using "plain HMR" in my post Practical Redux, Part 3: Project Setup.
It's important to note that none of this requires the React-HotLoader tool!. This only requires Webpack (and to be nitpicky, I believe Browserify can be tweaked to offer HMR as well).
React-Hot-Loader uses the Webpack HMR API in a very powerful, but complex way. RHL transforms your code as it is compiled to add "proxy" components that wrap around anything that looks like a React component. Those "proxy" components insert their own module.hot.accept()
calls at a much finer-grained level, to try to catch updates to each specific React component file instead of letting the updates "bubble up" to the root component file. The "proxy" components also try to manage the component state for the "real" components, so that when the "real" components get swapped out, the component state is carried over.
RHL is great... when it works. Unfortunately, there's a lot of edge cases involved, and that causes a lot of problems. Variations in build config can cause RHL to not work right. This is one of the reasons why Dan Abramov originally stopped working on RHL and started Create-React-App - to provide a stable, consistent, and known build environment as a potential baseline for later revisiting the idea of making advanced hot reloading widely available.
I personally advise that people not use React-Hot-Loader, and instead stick with "plain HMR". With "plain HMR", updates to your component tree will not keep component state between reloads, but the implementation is much simpler and more reliable. Also, Redux goes great with plain HMR, as any data that's kept in Redux is still there when you reload the component tree from edits, and so the app will re-render based on that Redux state.
Further Information 🔗︎
This is a post in the Blogged Answers series. Other posts in this series:
- Aug 08, 2023 - Blogged Answers: My Experience Modernizing Packages to ESM
- Jul 06, 2022 - Blogged Answers: How I Estimate NPM Package Market Share (and how Redux usage compares to other libraries)
- Jun 22, 2021 - Blogged Answers: The Evolution of Redux Testing Approaches
- Jan 18, 2021 - Blogged Answers: Why React Context is Not a "State Management" Tool (and Why It Doesn't Replace Redux)
- Jun 21, 2020 - Blogged Answers: React Components, Reusability, and Abstraction
- May 17, 2020 - Blogged Answers: A (Mostly) Complete Guide to React Rendering Behavior
- May 12, 2020 - Blogged Answers: Why I Write
- Feb 22, 2020 - Blogged Answers: Why Redux Toolkit Uses Thunks for Async Logic
- Feb 22, 2020 - Blogged Answers: Coder vs Tech Lead - Balancing Roles
- Jan 19, 2020 - Blogged Answers: React, Redux, and Context Behavior
- Jan 01, 2020 - Blogged Answers: Years in Review, 2018-2019
- Jan 01, 2020 - Blogged Answers: Reasons to Use Thunks
- Jan 01, 2020 - Blogged Answers: A Comparison of Redux Batching Techniques
- Nov 26, 2019 - Blogged Answers: Learning and Using TypeScript as an App Dev and a Library Maintainer
- Jul 10, 2019 - Blogged Answers: Thoughts on React Hooks, Redux, and Separation of Concerns
- Jan 19, 2019 - Blogged Answers: Debugging Tips
- Mar 29, 2018 - Blogged Answers: Redux - Not Dead Yet!
- Dec 18, 2017 - Blogged Answers: Resources for Learning Redux
- Dec 18, 2017 - Blogged Answers: Resources for Learning React
- Aug 02, 2017 - Blogged Answers: Webpack HMR vs React-Hot-Loader
- Sep 14, 2016 - How I Got Here: My Journey Into the World of Redux and Open Source