Differences between React useMemo and useCallback and Which to use?

Two essential tools in the React developer’s arsenal for optimizing performance are the useMemo and useCallback hooks. While both are used to enhance performance by memoizing values and functions, respectively, they serve distinct purposes and understanding their differences is crucial for effective React development.

useMemo Hook

The ‘useMemo‘ hook is used for memoizing expensive calculations so that they are only re-executed when one of the dependencies has changed. It returns a memoized value.

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

In this syntax:

  • The first argument is a function that computes a value.
  • The second argument is an array of dependencies. The memoized value will be recalculated only when one of these dependencies changes.

useCallback Hook

The ‘useCallback‘ hook is used to memoize callbacks to prevent unnecessary re-renders of child components that rely on them. It returns a memoized callback function.

Syntax:

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

In this syntax:

  • The first argument is a callback function.
  • The second argument is an array of dependencies. The memoized callback will only be re-created if one of these dependencies changes.

Example

Let’s consider an example where we have a parent component that renders a child component, and this child component has a callback function as a prop. We’ll use both 'useMemo‘ and ‘useCallback' in this example to demonstrate their differences.

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

const ParentComponent = () => {
  const [count, setCount] = useState(0);

  // Using useMemo to memoize the result of the expensive calculation
  const memoizedValue = useMemo(() => {
    console.log("Expensive calculation performed!");
    return count * 2;
  }, [count]);

  // Using useCallback to memoize the callback function
  const memoizedCallback = useCallback(() => {
    console.log("Callback invoked!");
    setCount(count + 1);
  }, [count]);

  return (
    <div>
      <p>Count: {count}</p>
      <p>Memoized Value: {memoizedValue}</p>
      <ChildComponent callback={memoizedCallback} />
    </div>
  );
};

const ChildComponent = ({ callback }) => {
  // Some child component rendering logic
  return (
    <div>
      <button onClick={callback}>Increment Count</button>
    </div>
  );
};

export default ParentComponent;
  • useMemo': In the 'ParentComponent‘, we’re memoizing the result of an expensive calculation (‘count * 2‘). This calculation will only be re-executed when the ‘count‘ state changes.
  • 'useCallback': We’re memoizing the callback function ‘memoizedCallback‘ in the ‘ParentComponent‘. This ensures that the callback function is not recreated on each render unless the ‘count' state changes. This is particularly useful when passing callbacks to child components to prevent unnecessary re-renders.

Conclusion

In summary, ‘useMemo' is used to memoize values, while ‘useCallback‘ is used to memoize callback functions. Both are used to optimize performance by avoiding unnecessary re-calculations or re-creations.