Performance optimization is a big topic, let’s take a look at performance optimization of the React Function component. Fine rendering refers to making the granularity of each render finer, rendering parts of the render, caching parts of the render that are not necessary,

setState

React goes through these steps from a SetState to a UI update:

Call SetState(update State) => render Function(component render, Function execution) => diff => commit => render Dom(update interface)

Each render does not necessarily result in an update to the page UI, which is diff optimized

We mainly talk about how to reduce the unnecessary render Function, reduce unnecessary component Function hanging.

He that would do well must sharpen his tools

  1. Install react DevTools first

  2. Turn on Highlight updates when Components Render in component-setting-General.

    This way you can see which components are rendered after setState

  3. Turn on Record Why each Component rendered while Profiling in component-setting-general Profiling.

    So you know what is causing the component to rerender

List Rendering Examples

Let’s take a common list rendering example where we want to update the num of the first item in the list by clicking a button

We might write the following code

const listData = [
  { id: 'id-1'.num: 1 },
  { id: 'id-2'.num: 2}]export const List = () = > {
  const [list, setList] = useState(listData)
  
  const handleUpdateFirstItem = () = > {
    const newList = [...list]
    newList[0] = { ...newList[0].num: Math.random() }
    Num = math.random () // newList[0].num = math.random () // newList[0].num = math.random (
    setList(newList)
  }

  return (
    <ul>
      {list.map((item) => (
        <li key={item.id}>Num : {item.num} {console.log(`renderItemId: ${item.id}`)}</li>
      ))}
      <button onClick={handleUpdateFirstItem}>Modify the first item</button>
    </ul>)}Copy the code

RenderItemId (id-1, id-2); renderItemId (renderItemId); renderItemId (renderItemId);

Fine list rendering + Memo cache component

PureComponent and memo. The memo works like the React.PureComponent, but in function components, it makes a shallow comparison between props and state. If there is no change, the component is not updated.

export const List = () = > {
  const [list, setList] = useState(listData)

  const handleUpdateFirstItem = () = > {
    const newList = [...list]
    newList[0] = { ...newList[0].num: Math.random() }
    // newList[0].num = math.random (); // If newList[0].num = math.random (
    setList(newList)
  }

  return (
    <ul>
      {list.map((item) => (
        <Item key={item.id} item={item}/>
      ))}
      <button onClick={handleUpdateFirstItem}>Modify the first item</button>
    </ul>)}const Item = React.memo(({ item }) = > {
  console.log('renderItemId: ' + item.id)
  return (
    <li>
      {item.num}
    </li>)})Copy the code

Click on the button and we can seerenderItemIdOnly theid-1Printed, see here, need to remember: function componentmemoAnd the class componentReact.PureComponent, is a good helper of performance optimization.

We need to make sure that the props passed to each Item component do not change as much as possible. For example, if you want to know if the current Item is selected, check on the List component, not the Item component. Item only has isActive props. Instead of passing the entire activeIdList to each Item to compare its ID, update the activeIdList prop will render each Item. The props only accepts isActive and only render Item if the value actually changes.

How to optimize with Event passing

Again, as a common requirement, we update the num of an item by clicking on it based on the list above

There are several ways we might do this:

Method one: thelistIntroduced to eachItem (Highly not recommended)

export const List = () = > {
  const [list, setList] = useState(listData)
  return (
    <ul>
      {list.map((item) => (
        <Item setList={setList} list={list} key={item.id} item={item}/>
      ))}
    </ul>)}const Item = React.memo(({ item, setList, list }) = > {
  const handleClick = () = > {
    const newList = [...list]
    const index = newList.findIndex((s) = >s.id === item.id) newList[index] = { ... newList[index],num: Math.random() }
    setList(newList)
  }

  console.log('renderItemId: ' + item.id)

  return (
    <li>
      {item.num}
      <button onClick={handleClick}>Click on the</button>
    </li>)})Copy the code

Why is it extremely not recommended? What we found was that we just had to redorenderCurrent item, but otherItemIt will also be updated.

React DevTools we can see that the list of props for each Item causes a re-render

Method 2: The update function is written in the parent component and useduseCallbackCache functionUnable to cache components

export const List = () = > {
  const [list, setList] = useState(listData)

  const handleChange = useCallback((id) = > {
    const newList = [...list]
    const index = newList.findIndex((item) = >item.id === id) newList[index] = { ... newList[index],num: Math.random() }

    setList(newList)
  }, [list])

  return (
    <ul>
      {list.map((item) => (
        <Item setList={setList} onClick={handleChange} key={item.id} item={item}/>
      ))}
    </ul>)}const Item = React.memo(({ item, onClick }) = > {

  const handleClick = useCallback(() = > {
    onClick(item.id)
  }, [item.id, onClick])

  console.log('renderItemId: ' + item.id)

  return (
    <li>
      {item.num}
      <button onClick={handleClick}>Click on the</button>
    </li>)})Copy the code

These twoItemOr do they all start overrenderFrom the analysis toolpropsIn theonClickThe function has changed becausehandleChangeEven if it doesuseCallbackCache, but because must depend onlistBut again every timesetListCause each incominghandleChangeIt’s new. It’s brokenmenoThe effect.

Method 3: Improved Method 2: Cache list

Method 2 is that because handleChange relies on list, the function is created every time, so we’ll try to cache it with ref.

export const List = () = > {
  const [list, setList] = useState(listData)

  // Cache the list with ref
  const ref = useRef(list)

  // listen for list changes stored in ref
  useEffect(() = > {
    ref.current = list
  }, [ref, list])

  const handleChange = useCallback((id) = > {
    const newList = [...ref.current]
    const index = newList.findIndex((item) = >item.id === id) newList[index] = { ... newList[index],num: Math.random() }

    setList(newList)
  }, [ref]) // deps relies on ref instead of list

  return (
    <ul>
      {list.map((item) => (
        <Item setList={setList} onClick={handleChange} key={item.id} item={item}/>
      ))}
    </ul>)}const Item = React.memo(({ item, onClick }) = >{... })Copy the code

So you can just click on whatever you wantrenderWhich one. But it’s kind of a hassle to write c every time.

Method 4:useEventCallBack (Recommended)

UseEventCallBack hook (); React (); useEventCallBack hook (); useEventCallBack hook ();

export const List = () = > {
  // ...
  const handleChange = useEventCallBack((id) = >{... },[list])return (
    // ...)}Copy the code

Method 5: UseuseReducer + useContext (Recommended method of multi-layer data transmission)

This method is suitable for multi-tier component structures, but I won’t say more.

conclusion

In a nutshell, re-render components that only need to be re-rendered as much as possible.

Back to the context of this article:

In general: try to make each component more granular, let the memo component cache. Keep the components props as constant as possible. However, some scenarios must cause the component render scenario, repeated memo comparison will also incur overhead, so the specific situation needs to be handled according to the business scenario.

Manual optimization: Generally, manual optimization is based on the specific business scenarios to compare the props. Sometimes, lodash pick,omit and other methods can be used to select the comparison fields, and then isEqual can be used to compare the values. It is important to note that these values and comparison calculations are also expensive, so you need to make trade-offs based on actual business scenarios

Reference documentation

Optimizing Performance React Official document