Remember that in v16.8 we introduced a new API called React Hooks, which use functions to render pages, rather than class-based components. We call it the Functional Component.
React rendering is actually very fast, and we don’t necessarily need to do special performance optimizations during development. However, in recent projects, images have been reloaded when the browser disables cache because of repeated rendering components, which is not an experience. So it’s time to learn how to deal with unnecessary repetitive rendering of function components!
This article mainly introduces the use of useMemo, Memo, useCallback, these three methods can basically deal with most of the repeat rendering problem. The usage of useMemo and useCallback is similar, requiring two parameters, the method to be cached, and the method’s dependency values to be updated when changes are made. Let’s go into details.
const value = useMemo(fnM, [a]);
const fnA = useCallback(fnB, [a]);
Copy the code
First, let’s be clear: why does repeated rendering exist?
This is because React Hook uses function components, and any change to the parent component causes the child component’s functions to execute, thus rerendering. The parent component does not pass props to its child component. The props passed by the parent component to its child component is of simple data type. The props passed by the parent component to its child component has complex data type. How do we deal with it?
1. The parent component has no props and passes the child component props
A quick introduction to this method: React.memo is a higher-order component. It is very similar to the React.PureComponent. By default, only shallow comparisons are made to objects of complex types. If you want to control the comparison process, you can pass in the comparison function as a second argument. The React. Memo (MyComp areEqual),
Does it look familiar? The same comparison function is available in shouldComponentUpdate, but! ⚠️ Note: in shouldComponentUpdate the method returns the opposite value!
Moving on: in this case, rendering of the child component does not depend on changes in the parent component’s value. Instead, use react.Memo to wrap the child component, caching the subcomponent. In this way, any changes in the value of the parent component will use the cached child component. Problem solving ~
2. The parent component passes props to the child component as simple datatype — use react. memo
Because the react. memo is shallow by default, using the react. Memo wrapped child component will shallow compare whether the props passed in have changed. Simple numerical type, shallow comparison can determine whether there is a change. If the props passed in does not change, the cached subcomponent is used, and if the props passed in changes, the component is rerendered. Problem solving ~
3. The parent component passes the props of the child component with complex datatypes — Memo, useMemo, useCallback
When we pass values to subcomponents through props, we may need to pass complex types such as object and values of type function. However, the Memo sub-component performs shallow comparison when rendering comparison. Even if we pass in the same object or function, the sub-component will think that the passed parameter has been modified, so the sub-component will re-render. It’s time to use useCallback and useMemo.
Let’s start with the more commonly used useCallback:
// Use memo only, the parent component passes the child component prop as the method,
// This method is called in the child component, changing the value of the parent component, causing the parent component to re-render.
// Because the parent component rerenders, the method passed to the child component will be considered modified due to its reference address, resulting in unnecessary rerenders of the child component.
const Child = memo((props) = > {
console.log('I'm a child component');
return (
<button onClick={props.handleClick}>Change the age in the parent component</button>)})const Father = () = > {
console.log('I'm a parent component')
const [age, setAge] = useState(0);
return(< div > < span > ` current count value is ${age} ` < span > < Child handleClick = {() = > setAge (age + 1)} / > < / div >)}Copy the code
// Use useCallback to optimize the function passed to the child component, initialize the function only once, Const Father = () => {console.log(' I'm a parent ') const [age, setAge] = useState(0); Return (< div > < span > ` at present age for ${age} ` < span > < Child handleClick = {useCallback (() = > setAge (age + 1), [])} / > < / div >)}Copy the code
⚠️ Note: pass the correct dependency value in the second argument to useCallback, otherwise useCallback will not be reexecuted and the variable used will be the same as before. The same is true of useMemo
We may use some parameters in the method but outside the method, we must put these parameters in the dependency, otherwise we will always use the cached method, inside the external parameters will always be the old value.
Now, an example of useMemo!
// Using memo and useCallback, we find that when we update the attribute profile as an object,
// The child executes even though it only changes the age value and does not use the age field.
// This is because in the case of the parent component updating other states, the child component's profile is a complex type,
// Simply making a shallow comparison will be perceived as a change and will keep re-rendering the change, causing the sub-component function to keep executing, which is also an unnecessary waste of performance.
// To solve this problem, use useMemo on profile properties
const Child = memo((props) = > {
console.log('I'm a child component');
const {profile, handleClick} = props;
return (
<div>
<div>${profile.name}, gender ${profile.gender} '}</div>
<button onClick={handleClick}>Change the parent component age</button>
</div>)})const Father = () = > {
console.log('I'm a parent component')
const [age, setAge] = useState(0);
const [name, setName] = useState('Zhang SAN Male');
const
return${age} '<span> <Child profile={{name, gender: name.indexof (' male ') > -1? 'male' : 'female'}} handleClick = {useCallback (() = > setAge (age + 1), [])} / > < / div >)}Copy the code
// Use useMemo to return the same object as the original object. The second argument is a dependency. A new object is generated only when the name is changed. The dependency must be filled in correctly, otherwise the profile will still use the old value when name changes. Const Father = () => {console.log(' I am a parent ') const [age, setAge] = useState(0); Const [name, setName] = useState(); Const return (<div> <span> 'current age is ${age}' <span> <Child profile={useMemo(() => ({name, gender: name.indexof (' male ') > -1? 'male' : 'female'}), [name])} handleClick = {useCallback (() = > setAge (age + 1), [])} / > < / div >)}Copy the code
Finally, a little summary:
- The child component has no props passed in from the parent component, or the props passed in just uses the memo for simple numeric types.
⚠️ Note: This higher-level component can be used in both function Component and class Component
- When a child component has a method passed in from the parent, the memo is used along with the useCallback wrapped around the method, which requires updated dependency values.
- When a child component has objects and array equivalents from the parent, the Memo is used along with useMemo to return the object as a method, passing in the dependency values that need to be updated.