Experience in performance optimization in React applications (2)

Original link: https://innei.ren/posts/programming/experience-in-performance-optimization-in-react-applications-2

This rendering is generated by marked, and there may be typography problems. For the best experience, please go to: https://innei.ren/posts/programming/experience-in-performance-optimization-in-react-applications-2

Last time I said that list components should be optimized in React applications, and how complex components should be written today. Jotai and Zustand are cooing, let’s talk about it next time.

Everyone who has written about Dachang Shishan should have experienced it. It is common for a component to have hundreds or even thousands of lines. It is also common to nest a component within a component. A brief summary of the following three do not:

  • Do not define other Components inside Component
  • Do not use render to render ReactNode, eg. render(data1, data2, data3) => ReactNode
  • Define a component without useCallback

What do you mean, simply list the above wrong writing, you must not learn:

 // ❌ 在组件内部定义其他组件function ParentComponent(props) { function ChildComponent() { return
我是子组件


}

return (


<ChildComponent />


)
}

// ❌ Render ReactNode with render:
function MyComponent({ data1, data2, data3 }) {
const render = (data1, data2, data3) => {
return

{data1}{data2}{data3}


}

return render(data1, data2, data3);
}

// ❌ define a component with useCallback
import React, { useCallback } from ‘react’;

function ParentComponent(props) {
const ChildComponent = useCallback(() => {
return

我是子组件


}, []);

return (


<ChildComponent />


)
}

The above is just a very simple example. No one will make such a mistake, because this component is very simple and easy to disassemble. How could such a novice code be written? In actual business scenarios, the complexity of components is much greater than this. Sometimes it seems that there is no way to extract some logic and you can only write this kind of novice code, resulting in all the states at the top of the component, and the components in the component Also because the re-render of the top-level component is always rebuilt, the performance is very low.

Eliminate other components defined inside the component

We define a slightly more complex, data-coupled business component that seems to have to use the positioning component in the component, and then optimize it.

Suppose we have an OrderForm component, which needs to dynamically generate different OrderItem components according to the incoming order information. At this time, you may want to define OrderItem component in OrderForm , as follows:

Wrong way:

 function OrderForm({ orders }) { const [myName, setMyName] = useState('Foo') function OrderItem({ order }) { return (

{order.name}


数量: {order.quantity}


单价: {order.price}


{myName}

)
}

// or the following form
const OrderItem = React. useCallback(({ order }) {
return (

{order.name}


数量: {order.quantity}


单价: {order.price}


{myName}

)
}, [order, myName])

return (

{orders.map(order =>
<OrderItem key={order.id} order={order} />
)}

);
}

But in doing so, OrderItem will be rebuilt every time OrderForm is rendered, which means that all OrderItem under this list traversal are re-mounted instead of re-rendered. In React, creating a new instance of a component (i.e. the first render of the component) usually takes more time than updating an existing instance of the component (i.e. the re-render of the component).

The correct way is to define OrderItem component outside OrderForm and pass data through props:

 function OrderItem({ order, myName }) { return (

{order.name}


数量: {order.quantity}


单价: {order.price}


{myName}

)
}

function OrderForm({ orders }) {
return (

{orders.map(order =>
<OrderItem key={order.id} order={order} myName={myName}/>
)}

);
}

In this way, no matter how OrderForm is rendered, OrderItem will only be defined once, and it can decide whether to re-render according to the change of props, thereby improving performance.

Remove components that pass parameters in render mode

Sometimes you will see some components define slots like this. For example, the following Table components.

 type Data = (string | number)[][]; type ColumnRenderer = (data: string | number, rowIndex: number, columnIndex: number) => React.ReactNode; interface TableProps { data: Data; columnRenderers: ColumnRenderer[]; } const Table: React.FC<TableProps> = ({ data, columnRenderers }) => { return (

{data.map((row, rowIndex) => (

{columnRenderers. map((render, columnIndex) => (

{render(row[columnIndex], rowIndex, columnIndex)}

))}

))}

);
};

The above writing method is not very good, first of all, render is a ReactNode returned by a function, so when you use this component, the incoming render cannot use React Hooks. For example:

{
useState() // ❌ 不能使用
return

{data.id}


}
]}

不仅不能使用Hooks,而且这样的写法会导致Table 组件re-render后,下面的所有列的render 全部重建。因为render 不是一个组件而是一个ReactNode,不能用作性能优化。


上述的Table 可以改写成:


 type Data = (string | number)[][]; interface TableColumnProps { data: string | number; render: (data: string | number) => React.ReactNode; } interface TableProps { data: Data; columnComponents: Array<React.FC<TableColumnProps>>; } const Table: React.FC<TableProps> = ({ data, columnComponents }) => { return (

{data.map((row, rowIndex) => (

{columnComponents. map((Component, columnIndex) => (
<Component key={columnIndex} data={row[columnIndex]} />
))}

))}

);
};

这样就可以在定义列组件时使用Hooks 了并且列组件只受到data的影响(前提需要给列组件套上memo),而且也只需要把原本的(data1, data2) =>改写成({ data1, data2 })就行了。


如:


{
return

{data.id}


})

今天先说到这。


上述部分代码和文字由GPT-4 编写。


看完了?说点什么呢

This article is transferred from: https://innei.ren/posts/programming/experience-in-performance-optimization-in-react-applications-2
This site is only for collection, and the copyright belongs to the original author.