What does useCallback/useMemo do in React?

ZiiMakc picture ZiiMakc · Nov 5, 2018 · Viewed 30.2k times · Source

As said in docs, useCallback Returns a memoized callback.

Pass an inline callback and an array of inputs. useCallback will return a memoized version of the callback that only changes if one of the inputs has changed. This is useful when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders (e.g. shouldComponentUpdate).

const memoizedCallback = useCallback(
  () => {
    doSomething(a, b);
  },
  [a, b],
);

But how does it work and where is the best to use it in React?

P.S. I think visualisation with codepen example will help everyone to understand it better. Explained in docs.

Answer

Yangshun Tay picture Yangshun Tay · Nov 6, 2018

This is best used when you want to prevent unnecessary re-renders for better performance.

Compare these two ways of passing callbacks to child components taken from React Docs:

1. Arrow Function in Render

class Foo extends Component {
  handleClick() {
    console.log('Click happened');
  }
  render() {
    return <Button onClick={() => this.handleClick()}>Click Me</Button>;
  }
}

2. Bind in Constructor (ES2015)

class Foo extends Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
  }
  handleClick() {
    console.log('Click happened');
  }
  render() {
    return <Button onClick={this.handleClick}>Click Me</Button>;
  }
}

Assuming <Button> is implemented as a PureComponent, the first way will cause <Button> to re-render every time <Foo> re-renders because a new function is created in every render() call. In the second way, the handleClick method is only created once in <Foo>'s constructor and reused across renders.

If we translate both approaches to functional components using hooks, these are the equivalents (sort of):

1. Arrow Function in Render -> Un-memoized callback

function Foo() {
  const handleClick = () => {
    console.log('Click happened');
  }
  return <Button onClick={handleClick}>Click Me</Button>;
}

2. Bind in Constructor (ES2015) -> Memoized callbacks

function Foo() {
  const memoizedHandleClick = useCallback(
    () => console.log('Click happened'), [],
  ); // Tells React to memoize regardless of arguments.
  return <Button onClick={memoizedHandleClick}>Click Me</Button>;
}

The first way creates callbacks on every call of the functional component but in the second way, React memoizes the callback function for you and the callback is not created multiple times.

In most cases, it's fine to do the first way. As the React docs state:

Is it OK to use arrow functions in render methods? Generally speaking, yes, it is OK, and it is often the easiest way to pass parameters to callback functions.

If you do have performance issues, by all means, optimize!