Function component optimization - React.memo
React.memo
React.memo(ReactNode, [(prevProps, nextProps) => {}])
- First parameter: component
- Second parameter: user defined comparison function. True is returned when two props are the same, and false if they are different. Returning true prevents updates, and returning false re renders.
If the component is wrapped in React.memo, then the component will render the same result in the same props case, so as to improve the performance of the component through the memory component rendering results.
React.memo only checks props changes. By default, it only makes shallow comparison on complex objects. If you want to control the comparison process, please pass in the custom comparison function through the second parameter.
const MyComponent = React.memo(function MyComponent(props) { /* Rendering with props */ }, (prevProps, nextProps) => { /* If nextProps is passed into the render method, the returned result is the same as If the return results of prevProps passed into render method are consistent, true will be returned, Otherwise, false is returned */ })
test
Background introduction
There are two sub components in a parent component. When the parent component is re rendered, will the two sub components be re rendered under different conditions?
Field interpretation
Field name | meaning | Test significance |
---|---|---|
title | Base type constant | Impact of test foundation type change on sub components |
commonObject | Reference type constant | Test the impact of reference type change on sub components; Test the impact of the reference type change defined by hook(useMemo) on the sub components |
dataSource | Reference type defined by useState | Test the impact of the change of reference type defined by hook(useState) on sub components |
updateXxxxInfo | method | Test the impact of reference type change on sub components; Test the impact of the reference type change defined by hook(useCallBack) on the sub components |
Basic code
Subcomponent BaseInfo:
const BaseInfo = (props) => { console.log('BaseInfo Re render, props: ', props) const { title, dataSource = {} } = props return ( <Card title={title}> <div>full name:{dataSource.name}</div> </Card> ) }
Subcomponent OtherInfo:
const OtherInfo = (props) => { console.log('OtherInfo Re render, props: ', props) const { title, dataSource } = props return ( <Card title={title}> <div>school:{dataSource.school}</div> </Card> ) }
Parent component FunctionTest:
function FunctionTest() { const [baseInfo, setBaseInfo] = useState({ name: 'chaos' }) const [otherInfo, setOtherInfo] = useState({ school: 'Shanghai University' }) return ( <Space direction="vertical" style={{ width: '100%' }}> <Space> <Button onClick={() => { console.log('click-Modify basic information') setBaseInfo({ name: 'brave troops' }) }} >Modify basic information</Button> <Button onClick={() => { console.log('click-Modify other information') setOtherInfo({ school: 'Peking University' }) }} >Modify other information</Button> </Space> <BaseInfo title="essential information - Subcomponents" dataSource={baseInfo} /> <OtherInfo title="Other information - Subcomponents" dataSource={otherInfo} /> </Space> ) }
Test 1: modify the sub component BaseInfo to React.memo package
const BaseInfo = React.memo((props) => { console.log('BaseInfo Re render, props: ', props) const { title, dataSource = {} } = props return ( <Card title={title}> <div>full name:{dataSource.name}</div> </Card> ) })
After clicking "modify basic information", BaseInfo and OtherInfo will be re rendered.
After clicking "modify other information", OtherInfo is re rendered, but BaseInfo is not re rendered.
Conclusion:
- When props is a basic type or a reference type defined by react hook (useState), use React.memo to prevent repeated rendering.
- BaseInfo of React.memo is used. When props are the same, there is no duplicate rendering.
Test 2: on the basis of test 1, add a reference type to the parent component FunctionTest and pass it to two child components
Test 2.1: when the constant of reference type is an object / array
function FunctionTest() { //... const commonObject = {} //... return ( // ... <BaseInfo title="essential information - Subcomponents" dataSource={baseInfo} commonObject={commonObject} /> <OtherInfo title="Other information - Subcomponents" dataSource={otherInfo} commonObject={commonObject} /> // ... ) }
Click "modify basic information" or "modify other information", and BaseInfo and OtherInfo will be re rendered.
Test 2.2: when the constant of reference type is a method:
function FunctionTest() { //... const updateBaseInfo = () => { console.log('Update basic information, original data:', baseInfo) setBaseInfo({ name: 'feastful' }) } const updateOtherInfo = () => { console.log('Update other information, original data:', otherInfo) setOtherInfo({ school: 'Henan University' }) } //... return ( //... <BaseInfo title="essential information - Subcomponents" dataSource={baseInfo} updateBaseInfo={updateBaseInfo} /> <OtherInfo title="Other information - Subcomponents" dataSource={otherInfo} updateOtherInfo={updateOtherInfo} /> //... ) }
Click "modify basic information" or "modify other information", and BaseInfo and OtherInfo will be re rendered.
Conclusion:
- When props contains reference types, repeated rendering cannot be prevented when React.memo is used and the comparison function is not customized.
- Whether React.memo is used or not, it will be re rendered. At this time, the performance of BaseInfo is not as good as OtherInfo because BaseInfo has one more diff.
Test 3: add hook on the basis of test 2
Test 3.1: add useMemo hook to commonObject
const commonObject = useMemo(() => {}, [])
After clicking "modify basic information", BaseInfo and OtherInfo will be re rendered.
After clicking "modify other information", OtherInfo is re rendered, but BaseInfo is not re rendered.
Test 3.2: add useCallback hook to updateBaseInfo and updateOtherInfo
const updateBaseInfo = useCallback(() => { console.log('Update basic information, original data:', baseInfo) setBaseInfo({ name: 'feastful' }) }, []) const updateOtherInfo = useCallback(() => { console.log('Update other information, original data:', otherInfo) setOtherInfo({ school: 'Henan University' }) }, [])
After clicking "modify basic information", BaseInfo and OtherInfo will be re rendered.
After clicking "modify other information", OtherInfo is re rendered, but BaseInfo is not re rendered.
Conclusion:
- When props' function uses useMemo/useCallback, it can prevent repeated rendering when React.memo is used and the comparison function is not customized.
- BaseInfo of React.memo is used. When props are the same, there is no duplicate rendering.
Test 4: on the basis of test 3, add React.memo to OtherInfo and customize the comparison function
const OtherInfo = React.memo((props) => { console.log('OtherInfo Re render, props: ', props) const { title, dataSource, updateOtherInfo } = props return ( <Card title={title}> <div>school:{dataSource.school}</div> <Button onClick={updateOtherInfo}>Update school</Button> </Card> ) }, (prevProps, nextProps) => { console.log('OtherInfo props compare') console.log('OtherInfo old props: ', prevProps) console.log('OtherInfo new props: ', nextProps) let flag = true Object.keys(nextProps).forEach(key => { let result = nextProps[key] === prevProps[key] console.log(`compare ${key}, The result is: ${result}`) if (!result) { flag = result } }) console.log(`OtherInfo assembly ${flag ? 'can't' : 'meeting'}Render`) return flag })
After clicking "modify basic information", BaseInfo is re rendered, but OtherInfo is not re rendered.
After clicking "modify other information", BaseInfo does not re render, OtherInfo re renders.
Conclusion:
- When props' function uses useMemo/useCallback, it can prevent repeated rendering when React.memo is used and the comparison function is not customized.
- The second parameter of React.memo can determine whether custom rendering is required.