Preface
Another article in this column talks about the fact that useMemo has certain overhead and cannot be abused. This article explains two simple and practical methods to optimize component rendering performance:
- Move down state
- Content enhancement
Before explaining these two methods, we need to manually create a component with serious rendering performance, as shown below:
import { useState } from 'react'; export default function App() { let [color, setColor] = useState('red'); return ( <div> <input value={color} onChange={(e) => setColor()} /> <p style={{ color }}>Hello, world!</p> <ExpensiveTree /> </div> ); } function ExpensiveTree() { let now = (); while (() - now < 100) { // Artificial delay -- do nothing for 100ms } return <p>I am a very slow component tree.</p>; }
Obviously, when the state within the App component changes, the ExpensiveTree component will re-render. In fact, the props and state of the ExpensiveTree component have not changed. This is not the result we expect. The following will provide two simple methods to improve the performance of component rendering;
Move down state
export default function App() { let [color, setColor] = useState('red'); return ( <div> <input value={color} onChange={(e) => setColor()} /> <p style={{ color }}>Hello, world!</p> <ExpensiveTree /> </div> ); }
We can see that the above ExpensiveTree component does not depend on the state inside the App component, so can we consider extracting the elements that depend on color into a component that depends on color?
Here is the code after practice:
export default function App() { return ( <> <Form /> <ExpensiveTree /> </> ); } function Form() { let [color, setColor] = useState('red'); return ( <> <input value={color} onChange={(e) => setColor()} /> <p style={{ color }}>Hello, world!</p> </> ); }
At this time, the element that depends on color is extracted into the Form component. The state inside the Form component no longer affects the rendering of the ExpensiveTree component, and the problem is solved.
Content enhancement
export default function App() { let [color, setColor] = useState('red'); return ( <div style={{ color }}> <input value={color} onChange={(e) => setColor()} /> <p>Hello, world!</p> <ExpensiveTree /> </div> ); }
The above situation is that the parent node of the high-overhead component ExpensiveTree depends on color. At this time, it is obvious that the downward state is not feasible, but there is another way:
export default function App() { return ( <ColorPicker> <p>Hello, world!</p> <ExpensiveTree /> </ColorPicker> ); } function ColorPicker({ children }) { let [color, setColor] = useState("red"); return ( <div style={{ color }}> <input value={color} onChange={(e) => setColor()} /> {children} </div> ); }
At this time, it is completely opposite to state promotion. Elements that depend on color are promoted to the ColorPicker component, while elements that do not depend on color are passed into ColorPicker in the form of children props. When the state inside the component changes:
- ColorPicker will re-render but it is still saved
- ColorPicker's children property has not changed React does not access that subtree. Therefore, ExpensiveTree does not re-render.
The methods provided in this article are not new ideas, and everyone must have practiced them, but what I want to express is that compared to the abuse of useMemo, we need to first consider using these two methods to improve component rendering performance;
This is the article about the state downward movement of React components and content improvement. For more related content to the state downward movement of React components, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!