Trouble with simple example of React Hooks useCallback

后端 未结 1 1424
粉色の甜心
粉色の甜心 2020-12-06 12:51

I\'m trying to make a simple example that follows the React Hooks example in the doc at page https://reactjs.org/docs/hooks-reference.html#usecallback

Without

相关标签:
1条回答
  • 2020-12-06 13:25

    The intent of useCallback is to be able to leverage props or state that are in the current scope and that may change on re-render. The dependency-array then tells React when you need a new version of the callback. If you are trying to memoize an expensive computation, you need to use useMemo instead.

    The example below demonstrates the differences between useCallback and useMemo and the consequences of not using them. In this example I am using React.memo to prevent Child from re-rendering unless its props or state change. This allows seeing the benefits of useCallback. Now if Child receives a new onClick prop it will cause a re-render.

    Child 1 is receiving a non-memoized onClick callback, so whenever the parent component re-renders, Child 1 always receives a new onClick function so it is forced to re-render.

    Child 2 is using a memoized onClick callback returned from useCallback and Child 3 is using the equivalent via useMemo to demonstrate the meaning of

    useCallback(fn, inputs) is equivalent to useMemo(() => fn, inputs)

    For Child 2 and 3 the callback still gets executed every time you click on Child 2 or 3, useCallback just ensures that the same version of the onClick function is passed when the dependencies haven't changed.

    The following part of the display helps point out what is happening:

    nonMemoizedCallback === memoizedCallback: false|true

    Separately, I'm displaying somethingExpensiveBasedOnA and a memoized version using useMemo. For demonstration purposes I'm using an incorrect dependency array (I intentionally left out b) so that you can see that the memoized version doesn't change when b changes, but it does change when a changes. The non-memoized version changes whenever a or b change.

    import ReactDOM from "react-dom";
    
    import React, {
      useRef,
      useMemo,
      useEffect,
      useState,
      useCallback
    } from "react";
    
    const Child = React.memo(({ onClick, suffix }) => {
      const numRendersRef = useRef(1);
      useEffect(() => {
        numRendersRef.current++;
      });
    
      return (
        <div onClick={() => onClick(suffix)}>
          Click Me to log a and {suffix} and change b. Number of Renders:{" "}
          {numRendersRef.current}
        </div>
      );
    });
    function App(props) {
      const [a, setA] = useState("aaa");
      const [b, setB] = useState("bbb");
    
      const computeSomethingExpensiveBasedOnA = () => {
        console.log("recomputing expensive thing", a);
        return a + b;
      };
      const somethingExpensiveBasedOnA = computeSomethingExpensiveBasedOnA();
      const memoizedSomethingExpensiveBasedOnA = useMemo(
        () => computeSomethingExpensiveBasedOnA(),
        [a]
      );
      const nonMemoizedCallback = suffix => {
        console.log(a + suffix);
        setB(prev => prev + "b");
      };
      const memoizedCallback = useCallback(nonMemoizedCallback, [a]);
      const memoizedCallbackUsingMemo = useMemo(() => nonMemoizedCallback, [a]);
      return (
        <div>
          A: {a}
          <br />
          B: {b}
          <br />
          nonMemoizedCallback === memoizedCallback:{" "}
          {String(nonMemoizedCallback === memoizedCallback)}
          <br />
          somethingExpensiveBasedOnA: {somethingExpensiveBasedOnA}
          <br />
          memoizedSomethingExpensiveBasedOnA: {memoizedSomethingExpensiveBasedOnA}
          <br />
          <br />
          <div onClick={() => setA(a + "a")}>Click Me to change a</div>
          <br />
          <Child onClick={nonMemoizedCallback} suffix="1" />
          <Child onClick={memoizedCallback} suffix="2" />
          <Child onClick={memoizedCallbackUsingMemo} suffix="3" />
        </div>
      );
    }
    
    const rootElement = document.getElementById("root");
    ReactDOM.render(<App />, rootElement);
    
    

    Here's a related answer: React Hooks useCallback causes child to re-render

    0 讨论(0)
提交回复
热议问题