6

From what i understood from React documentation and other material across web, useCallback is used to avoid re-rendering of child component by ensuring that memoized version of callback is passed to it, thus referentially props remain same for child component. But all this is valid only if I am using React.memo on child component. Without React.memo, child component would re-render anyways. My question is what use is useCallback in this case i.e. without React.memo applied to child component. What are the other benefits of useCallback?

Ravi Chaudhary
  • 660
  • 6
  • 22
  • check useCallback section https://dev.to/dinhhuyams/introduction-to-react-memo-usememo-and-usecallback-5ei3 – bapafes482 Jan 09 '20 at 11:57
  • This link also talks about the same thing. My question is: is there any other use case for useCallback other than avoiding re-render of a child component. – Ravi Chaudhary Jan 09 '20 at 17:31

2 Answers2

1

React.memo ensures that a shallow comparison is performed when props enter a component and skips rendering of the component when they are equal.

Given a child component Cell: When applied to a function component that is created during the render of another, parent component, a new Cell component will be created on each render. This new Cell component will always make shallow comparisons on its props but it will be re-rendered on every render of its parent.

useCallback will however memoize this function callback Cell if it's dependency array does not change during a parent re-render. Alone a function component Cell wrapped in a useCallback will always re-render when it receives props, which will happen on every render of its parent. The difference however is that it's entire subtree is re-rendered if the component itself is recreated, as is the case when using React.memo by itself.

Paired together you can however get around re-rendering components defined inside of a parent.

As you will notice, when attempting to drag the Memoized cell, the one not wrapped in useCallback, no dragging happens. That is because the original element you attempt to drag is recreated as a new instance when it's parent re-renders. This concept is explained in more detail here

Example

const { useCallback, useState, useRef } = React;

function App() {
  const [state, setState] = useState(false);
  const [title, setTitle] = useState("");

  const Cell = useCallback(({ title }) => {
    console.log(`${title} rendering`);

    function onDragStart() {
      setState(true);
    }
    function onDragEnd() {
      setState(false);
    }

    return (
      <div draggable onDragStart={onDragStart} onDragEnd={onDragEnd}>
        Drag {title}
      </div>
    );
  }, []);

  const MemoizedCell = React.memo(({ title }) => {
    console.log(`${title} rendering`);

    function onDragStart() {
      setState(true);
    }
    function onDragEnd() {
      setState(false);
    }

    return (
      <div draggable onDragStart={onDragStart} onDragEnd={onDragEnd}>
        Drag {title}
      </div>
    );
  });

  const MemoizedCallbackCell = useCallback(
    React.memo(({ title }) => {
      console.log(`${title} rendering`);

      function onDragStart() {
        setState(true);
      }
      function onDragEnd() {
        setState(false);
      }

      return (
        <div draggable onDragStart={onDragStart} onDragEnd={onDragEnd}>
          Drag {title}
          <button onClick={() => setTitle(" Updated")}>Change Title</button>
        </div>
      );
    }),
    []
  );

  return (
    <div className="App">
      <Cell title="Cell" />
      <MemoizedCell title="Memoized Cell" />
      <MemoizedCallbackCell title={"Memoized Callback Cell" + title} />
      Is dragging {`${state}`}
      <br />
    </div>
  );
}

ReactDOM.render(<App />, document.getElementById('root'));
1

You might also need to pass a function to useEffect without changing useEffect on every render

For example:

import { useCallback, useEffect } from "react";

function useExample() {
  function fn() {
    console.log("a function");
  }
  const callback = useCallback(fn, []);

  useEffect(() => {
    callback();
  }, [callback]);
}

Real-world example with useDebounce:

David
  • 4,191
  • 2
  • 31
  • 40