This article mainly summarizes and organizes some performance optimization methods that can be used in React applications.
Preface
Purpose
At present, a large number of projects are carried out using react. Understanding and mastering the performance optimization of react has great benefits to the experience and maintainability of the project. The following describes some of the properties that can be used in react Optimization method
Performance optimization ideas
From the perspective of class components and functional components, you can think about how to optimize performance from the following aspects
- Reduce the number of re-renders
- Reduce rendering nodes
- Reduce the amount of rendering calculations
- Reasonable design components
Reduce the number of re-renders
One of the most time-consuming places in React is reconciliation (the ultimate goal of reconciliation is to update the UI according to the new state in the most effective way, we can simply understand it as diff). If render is not executed, there is no need reconciliation, so it can be seen that reducing the importance of render in the performance optimization process.
PureComponent
React.PureComponent is very similar to React.Component. The difference between the two is that React.Component does not implement shouldComponentUpdate(), while React.PureComponent implements this function in a shallow comparison of prop and state.
need to pay attention to in the use of PureComponent components, when the property value of props or state is an object, unnecessary rendering cannot be prevented, because the automatically loaded shouldComponentUpdate does only shallow comparison, so I want to To use the features of PureComponent, you should abide by the principles:
- Make sure the data type is a value type
- If it is a reference type, there should be no deep data changes (deconstruction)
ShouldComponentUpdate
This event can be used to determine when the component needs to be re-rendered. If the component props is changed or setState is called, this function returns a Boolean value. If it is true, the component will be re-rendered, otherwise, the component will not be re-rendered.
In both cases the component will be re-rendered. We can place a custom logic in this life cycle event to decide whether to call the render function of the component.
Here is a small example to help understand:
example, you want to display the detailed information of students in your application. Each student contains multiple attributes, such as name, age, hobby, height, weight, home address, Parent's name, etc.; in this component scenario, only the student's name, age, and address need to be displayed, and other information does not need to be displayed here, so in an ideal situation, information change components other than name, age, and address are not needed Re-rendered;
sample code is as follows:
import React from "react";
export default class ShouldComponentUpdateUsage extends React.Component {
constructor(props) {
super(props);
this.state = {
name: "小明",
age: 12,
address: "xxxxxx",
height: 165,
weight: 40
}
}
componentDidMount() {
setTimeout(() => {
this.setState({
height: 168,
weight: 45
});
}, 5000)
}
shouldComponentUpdate(nextProps, nextState) {
if(nextState.name !== this.state.name || nextState.age !== this.state.age || nextState.address !== this.state.address) {
return true;
}
return false;
}
render() {
const { name, age, address } = this.state;
return (
<div>
<p>Student name: {name} </p>
<p>Student age:{age} </p>
<p>Student address:{address} </p>
</div>
)
}
}
According to the React team, shouldComponentUpdate is an emergency exit that guarantees performance. Since it is an emergency exit, it means that we can't easily use it. But since there is such an emergency exit, it shows that it is still necessary sometimes. So we have to figure out when we need to use this emergency exit.
Principles of Use
When you feel that the changed state or props do not need to update the view, you should think about whether to use it.
that 161285a9c43c9a needs to pay attention to is: after the change, there is no need to update the state of the view, and it should not be placed in the state.
shouldComponentUpdate also comes at a price. If it is not handled well, it will consume more performance than multiple renders at a time, and it will also increase the complexity of the component. In general, PureComponent can be used;
React.memo
If your component renders the same result under the same props, then you can call it by wrapping it in React.memo to improve the performance of the component by memorizing the rendering result of the component. This means that in this case, React will skip the operation of rendering the component and directly reuse the result of the most recent rendering.
React.memo only checks the props change . If the functional component is wrapped by React.memo and its implementation has a hook of useState, useReducer or useContext, it will still be re-rendered when the state or context changes.
only performs shallow comparisons on complex objects. If you want to control the comparison process, please pass in a custom comparison function through the second parameter.
function MyComponent(props) {
/* 使用 props 渲染 */
}
function areEqual(prevProps, nextProps) {
/*
如果把 nextProps 传入 render 方法的返回结果与
将 prevProps 传入 render 方法的返回结果一致则返回 true,
否则返回 false
*/
}
export default React.memo(MyComponent, areEqual);
Note
The difference with the shouldComponentUpdate() method in the class component is that if the props are equal, areEqual will return true; if the props are not equal, it will return false. This is the opposite of the return value of the shouldComponentUpdate method.
Reasonable use of Context
Context provides a method for data transfer between component trees without manually adding props for each layer of components. It is precisely because of this feature that it can penetrate the comparison of React.memo or shouldComponentUpdate, that is to say, once the Value of the Context changes, all components that depend on the Context will be forceUpdated. This is responsive with Mobx and Vue Different from the system, the Context API cannot detect which components depend on which state in a fine-grained manner.
in principle
- Context only defines attributes that are shared by most components, such as the current user's information, theme, or selected language.
Avoid anonymous functions
First look at the following code
const MenuContainer = ({ list }) => (
<Menu>
{list.map((i) => (
<MenuItem key={i.id} onClick={() => handleClick(i.id)} value={i.value} />
))}
</Menu>
);
The above writing method seems relatively concise, but there is a potential problem that the anonymous function will have a different reference each time it is rendered, which will cause the Menu component to have the problem of repeated rendering; you can use useCallback to optimize:
const MenuContainer = ({ list }) => {
const handleClick = useCallback(
(id) => () => {
// ...
},
[],
);
return (
<Menu>
{list.map((i) => (
<MenuItem key={i.id} id={i.id} onClick={handleClick(i.id)} value={i.value} />
))}
</Menu>
);
};
Reduce rendering nodes
Lazy loading of components
Lazy loading of components allows the react application to display the component when it really needs to be displayed, which can effectively reduce the number of nodes to be rendered and increase the loading speed of the page.
React officially introduced new features after the 16.6 version: React.lazy and React.Suspense, these two components can be used together to facilitate the implementation of component lazy loading;
React.lazy
The main function of this method is You can define a dynamically loaded component, which can directly reduce the volume of the packaged bundle, and can delay loading components that do not need to be rendered during the first rendering. The code example is as follows:
before use
import SomeComponent from './SomeComponent';
After use
const SomeComponent = React.lazy(() => import('./SomeComponent'));
Using React.lazy's dynamic introduction feature requires the JS environment to support Promise. In IE11 and below browsers, you need to introduce a polyfill to use this feature.
React.Suspense
The main function of this component currently is to coordinate with the rendering of the lazy component, so that loading elements can be displayed while waiting for the lazy component to be loaded, and the user experience will not be directly blanked; the fallback attribute in the
The React element you want to display during component loading. You can place Suspense components anywhere on top of lazy-loaded components, and you can even wrap multiple lazy-loaded components with one Suspense component.
code example is as follows:
import React, { Suspense } from 'react';
const OtherComponent = React.lazy(() => import('./OtherComponent'));
const AnotherComponent = React.lazy(() => import('./AnotherComponent'));
function MyComponent() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<section>
<OtherComponent />
<AnotherComponent />
</section>
</Suspense>
</div>
);
}
One thing to pay special attention to is that React.lazy and Suspense technologies do not yet support server-side rendering. If you want to use it in an application that uses server-side rendering, it is recommended to use Loadable Components , which can be viewed in conjunction with this document server-side rendering packaging guide .
In addition, there are some in the industry react more mature open source library component lazy loading: react-Loadable and react-lazyload , interest can be combined with a look;
Virtual list
The virtual list is a technology that renders a certain part of the data in the long list according to the visible area of the scrolling container element. In the development of some projects, you will encounter some scenarios where the list data is not directly paginated to load the list data. In this case Next, consider combining the virtual list for optimization, which can display a certain part of the long list data according to the height of the container element and the height of the list item element, instead of rendering the long list completely, to improve the performance of infinite scrolling.
can focus on decentralizing two more commonly used libraries for in-depth understanding
Reduce the amount of rendering calculations
useMemo
Let's first look at the basic usage of useMemo:
function computeExpensiveValue(a, b) {
// 计算量很大的一些逻辑
return xxx
}
const memoizedValue = useMemo(computeExpensiveValue, [a, b]);
The first parameter of useMemo is a function. The value returned by this function will be cached. At the same time, this value will be used as the return value of useMemo. The second parameter is an array dependency. If the value in the array changes, it will Re-execute the function in the first parameter, and cache the value returned by the function as the return value of useMemo.
Note
- If no dependency array is provided, useMemo will calculate a new value each time it is rendered;
- If the calculation amount is small, you can also choose not to use useMemo, because this optimization will not be the main point of the performance bottleneck. Instead, the wrong use may cause some performance problems.
Use key when traversing the display view
The key helps React recognize which elements have changed, such as being added or deleted. Therefore, you should assign a certain identifier to each element in the array.
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
);
Precautions for using key:
- It is best if this element has a unique string in the list. Usually, we use the id in the data as the key of the element. When the id of the element is not determined, you can use the element index as the key as a last resort.
- The key of the element is meaningful only if it is placed in the context of the nearest array. For example, if you extract a ListItem component, you should keep the key on the <ListItem /> element in the array instead of putting it on the <li> element in the ListItem component.
Reasonable design components
Simplify props
If the props of a component are more complex, it will affect the efficiency of shallowCompare, and it will also make the component difficult to maintain. In addition, it does not conform to the principle of "single responsibility", so you can consider disassembling it.
Simplify State
When designing the state of a component, you can follow this principle: the data that requires the component to respond to its changes or needs to be rendered in the view can be placed in the state; this can avoid unnecessary data changes that cause the component to re-render.
Reduce component nesting
Generally unnecessary node nesting is caused by the abuse of high-level components/RenderProps. So the sentence "use xxx only when necessary". There are many ways to replace higher-order components/RenderProps, such as props, React Hooks
refer to
https://react.docschina.org/docs/optimizing-performance.html
https://www.infoq.cn/article/2016/07/react-shouldcomponentupdate
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。