React performance optimization - avoid duplicate rendering

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 namemeaningTest significance
titleBase type constantImpact of test foundation type change on sub components
commonObjectReference type constantTest 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
dataSourceReference type defined by useStateTest the impact of the change of reference type defined by hook(useState) on sub components
updateXxxxInfomethodTest 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.

Tags: React Optimize

Posted on Wed, 24 Nov 2021 06:55:49 -0500 by WiseGuy