UNPKG

4.92 kB Markdown View Raw
1---
2title: Hot Module Replacement
3---
4
5# Hot Module Replacement
6
7[MODES: framework]
8
9<br/>
10<br/>
11
12Hot Module Replacement is a technique for updating modules in your app without needing to reload the page.
13It's a great developer experience, and React Router supports it when using Vite.
14
15HMR does its best to preserve browser state across updates.
16For example, let's say you have form within a modal and you fill out all the fields.
17As soon as you save any changes to the code, traditional live reload would hard refresh the page causing all of those fields to be reset.
18Every time you make a change, you'd have to open up the modal _again_ and fill out the form _again_.
19
20But with HMR, all of that state is preserved _across updates_.
21
22## React Fast Refresh
23
24React already has mechanisms for updating the DOM via its [virtual DOM][virtual-dom] in response to user interactions like clicking a button.
25Wouldn't it be great if React could handle updating the DOM in response to code changes too?
26
27That's exactly what [React Fast Refresh][react-refresh] is all about!
28Of course, React is all about components, not general JavaScript code, so React Fast Refresh only handles hot updates for exported React components.
29
30But React Fast Refresh does have some limitations that you should be aware of.
31
32### Class Component State
33
34React Fast Refresh does not preserve state for class components.
35This includes higher-order components that internally return classes:
36
37```tsx
38export class ComponentA extends Component {} // ❌
39
40export const ComponentB = HOC(ComponentC); // ❌ Won't work if HOC returns a class component
41
42export function ComponentD() {} // ✅
43export const ComponentE = () => {}; // ✅
44export default function ComponentF() {} // ✅
45```
46
47### Named Function Components
48
49Function components must be named, not anonymous, for React Fast Refresh to track changes:
50
51```tsx
52export default () => {}; // ❌
53export default function () {} // ❌
54
55const ComponentA = () => {};
56export default ComponentA; // ✅
57
58export default function ComponentB() {} // ✅
59```
60
61### Supported Exports
62
63React Fast Refresh can only handle component exports. While React Router manages [route exports like `action`, ` headers`, `links`, `loader`, and `meta`][route-module] for you, any user-defined exports will cause full reloads:
64
65```tsx
66// These exports are handled by the React Router Vite plugin
67// to be HMR-compatible
68export const meta = { title: "Home" }; // ✅
69export const links = [
70 { rel: "stylesheet", href: "style.css" },
71]; // ✅
72
73// These exports are removed by the React Router Vite plugin
74// so they never affect HMR
75export const headers = { "Cache-Control": "max-age=3600" }; // ✅
76export const loader = async () => {}; // ✅
77export const action = async () => {}; // ✅
78
79// This is not a route module export, nor a component export,
80// so it will cause a full reload for this route
81export const myValue = "some value"; // ❌
82
83export default function Route() {} // ✅
84```
85
86👆 Routes probably shouldn't be exporting random values like that anyway.
87If you want to reuse values across routes, stick them in their own non-route module:
88
89```ts filename=my-custom-value.ts
90export const myValue = "some value";
91```
92
93### Changing Hooks
94
95React Fast Refresh cannot track changes for a component when hooks are being added or removed from it, causing full reloads just for the next render. After the hooks have been updated, changes should result in hot updates again. For example, if you add a `useState` to your component, you may lose that component's local state for the next render.
96
97Additionally, if you are destructuring a hook's return value, React Fast Refresh will not be able to preserve state for the component if the destructured key is removed or renamed.
98For example:
99
100```tsx
101export default function Component({ loaderData }) {
102 const { pet } = useMyCustomHook();
103 return (
104 <div>
105 <input />
106 <p>My dog's name is {pet.name}!</p>
107 </div>
108 );
109}
110```
111
112If you change the key `pet` to `dog`:
113
114```diff
115 export default function Component() {
116- const { pet } = useMyCustomHook();
117+ const { dog } = useMyCustomHook();
118 return (
119 <div>
120 <input />
121- <p>My dog's name is {pet.name}!</p>
122+ <p>My dog's name is {dog.name}!</p>
123 </div>
124 );
125 }
126```
127
128then React Fast Refresh will not be able to preserve state `<input />` ❌.
129
130### Component Keys
131
132In some cases, React cannot distinguish between existing components being changed and new components being added. [React needs `key`s][react-keys] to disambiguate these cases and track changes when sibling elements are modified.
133
134[virtual-dom]: https://reactjs.org/docs/faq-internals.html#what-is-the-virtual-dom
135[react-refresh]: https://github.com/facebook/react/tree/main/packages/react-refresh
136[react-keys]: https://react.dev/learn/rendering-lists#why-does-react-need-keys
137[route-module]: ../start/framework/route-module