The react useCallback refers to the north

The basic use

useCallback(callback: T, deps: DependencyList): callback

Passing the inline callback function and the array of dependencies as arguments to useCallback returns the Memoized version of the callback function, which is updated only when a dependency changes. This is useful when you pass callback data to child components that are optimized and use reference equality to avoid unnecessary rendering (such as shouldComponentUpdate).

The problem

What data is cached in the function returned by useCallback

import React, { useCallback, useState } from 'react';

class Child extends React.Component {
  shouldComponentUpdate(nextProps) {
    returnnextProps.flag ! = =this.props.flag;
  }

  render() {
    console.log('Child render');
    return <div>Child Count: {this.props.count}</div>; }}const UseCallBack = (a)= > {
  const [count, setCount] = useState(0);
  const [selfCount, setSelfCount] = useState(100);

  const memoizedCallback = useCallback((a)= > {
    console.log('count change', count, selfCount);
  }, [count]);

  return (
    <div>
      <Child count={count} flag={memoizedCallback} />
      <p>The self Count: {selfCount}</p>
      <p onClick={()= > setCount(count + 1)}>child count add</p>
      <p onClick={()= > setSelfCount(selfCount + 1)}>self count add</p>
      <p onClick={()= > memoizedCallback()}>callback click</p>
    </div>
Copy the code

It can be seen that when we change selfCount, since only count is monitored in our DEPS, the returned memoizedCallback is unchanged, and Child does not have re-render. When memoizedCallback is executed, the selfCount printed remains unchanged.

conclusion

The function returned by useCallback is an Memoized version of the first function passed in, and caches all values of useState. The return value of the function is updated only if dePS changes, and the value of useState inside it is updated.

Usage scenarios

function Counter() {
  const [count, setCount] = useState(0);
  const handleIncr = (a)= > {
    setCount(c + 1);
  };

  return (
    <div>
      {count} <Child onClick={handleIncr} />
    </div>
  );
}
Copy the code

Assuming Child is a very complex component, we increment count each time we click on it, triggering the group to be rerendered. Because Counter regenerates handleIncr every time it renders, it also causes the Child to rerender, whether it is wrapped in PureComponent or react.Memo.

Here I’m using useCallback to cache our handleIncr.

function Counter() {
  const [count, setCount] = useState(0);
  const handleIncr = useCallback((a)= > {
    setCount(count + 1);
  }, [count]);

  return (
    <div>
      {count} <Child onClick={handleIncr} />
    </div>
  );
}
Copy the code

Regenerating handleIncr only when the count changes avoids some performance issues, but if you need to listen for too many variables, such as useCallback(fn, [a, B, C, d]), it can clutter up the code. So what we’re going to do here is we’re going to change the way we think about setCount, instead of using variables in closures, we’re going to use functions to get the latest values.

const Child = React.memo(({ onClick }) => {
  console.log('child render');
  return <div onClick={onClick}>click</div>;
});

function Counter() {
  const [count, setCount] = useState(0);
  const handleIncr = useCallback(() => {
    setCount(c => c + 1); } []);return (
    <div>
      {count} <Child onClick={handleIncr} />
    </div>
  );
}
Copy the code

This way handleIncr does not change every time Counter re-render is performed, and the value of each operation is the latest