[React] the simplest way to reduce repetitive rendering in history, is it so simple?

May 31, 2023 1190hotness 0likes 0comments

Let's talk about the conclusion first

useCallback, useMemo, memo, shouldUpdate, performance optimized API, you should not use it without performance problems. Blind use will lead to negative optimization and even delay closure bug. This conclusion is a consensus.

but this is only one aspect. I mainly want to talk about it to the point, except for the ideas that API,React really reduces rerender.

the coarse granularity of React may hurt innocent people. The agility of jsx, the culprit, makes it difficult for props to distinguish between change and immutability

App is a component and Box is a subcomponent

 const App = ()=>{
   return <Box h="200px" w="200px" {/*.....*/}>hello</Box>
 }
 // after jsx is compiled
 const App = ()=>{ 
   return /*#__PURE__*/ React.createElement(Box, { h: "200px", w: "200px" }, "hello"); 
 };

The strategy adopted by React is congruent comparison. Props compares {h: "200px", w: "200px"} in the first time with {h: "200px", w: "200px"} in the second round. Although it seems to be the same, function creates a new object type every time, with a different address, rendering even empty objects, like flood discharge, rendering from the top of the head to the soles of the feet. Box will be rendered

 const App = () => {
   return <Box>hello</Box>  // Box without memo, still render
 }
 // after jsx is compiled
 const App = () => { 
   // props is an empty object here
   return /*#__PURE__*/ React.createElement(Box, {}, "hello"); 
 };

this is React's App-level update policy. Why do you adopt such a strategy? Because there are too many props, suppose it is a complex props

{h: "200px", w: "200px" more than 200 key-value} are omitted here

if I compare diff for half a day, it turns out that only one prop has changed, and I still want to let the self-component rerender, so simply rerender it.

this is foolproof, but such coarse granularity is good for

props changes infrequently or passes fewer parameters, and components with expensive rerender performance are not very friendly.

After all, is a big framework, and although such corner problems exist, I still have to deal with them, so I expose a series of api of shouldUpdate and leave the problems to the developers.

if you look at the template on the Vue side, it is not difficult to see from the way you write it that you can directly find the change and the constant.

 <Box w="50px" h="50px" :color="color" />

how to solve this problem?

in this example, thanks to the flexibility of the JSX syntax, React uses App-level updates and does not distinguish between props. Once the App root triggers the update, the flood discharge begins.

 const App = () => {
   const [count, setCount] = useState(0);
   return (
     <>
       <button onClick={() => setCount((a) => a + 1)}>Update</button>
       <A />{/ * or<A width="50px"/>, are all the same props * /}
<B count={count}/>
     </>
   );
 };

solution 1 state sinking

now that you know about the state improvement, I'd like to talk about the state sinking.

is very simple, contrary to status improvement, we only use count in A, so we should move const [count, setCount] = useState (0); move to B, move the small tree, don't move the big tree.

 const App = () => {
   return (
     <>
       <A />
       <B />
     </>
   );
 };

The reason for this is that

The component splits around the state to achieve the corresponding relationship between State and View

(PS: this is basically impossible to complete perfectly in React, even if the correspondence is not very accurate, whether it is big or not.)

Vue and Solid use proxy to focus on state, dependency tracing, and the corresponding relationship between State and View is finer in granularity and better in performance.

solution 2 shouldUpdate pruning

the principle of using component-optimized api,shouldComponentUpdate, memo, PureComponent, etc., is a high-level component, which is equivalent to creating a parent node for A, the isolation layer.

in this case, component A does not pass props and cannot be changed by the parent. Setting memo results in positive benefits, and the size of benefits depends on the complexity of component A.

solution 3 Provider + State Management

State sinking allows state and view to correspond one by one to test the skills of layout components

while the layout component, Provider can be a good auxiliary prop. Officials also said that excellent components must have Provider

how do I improve my status?

This is the principle of React state management libraries such as Redux, which is proved by adding context.Provider to the root of App (it is also a smart caching mechanism with the same life cycle as App). In theory, writing state to the management library and returning most components to stateless components can improve performance.

the disadvantage is that state and method that change state are combined into a data model, which I call a model. If you use only one library to manage all the state, all kinds of method are very confusing, poor readability and difficult to maintain.

so being able to create multiple model and use hook to split the Pinia of model is a very good state management solution.

 const App = ()=>{
    return {....}
 }
 export default ()=>(
   <So and soXProvider>
     <App/>
   </ so and soXProvider>
 )

const Leaf = memo(() => {
   const title = useContext(myContext);
   return <span>{title}</span>
})

A sufficiently small leaf node hollowed out the trunk and reduced the rerender in the middle.

InterServer Web Hosting and VPS

Aaron

Hello, my name is Aaron and I am a freelance front-end developer

Comments