UNPKG

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