DEV Community

Cover image for `useCallback` vs `useMemo` Hooks
Sivasubramaniyam
Sivasubramaniyam

Posted on • Edited on

`useCallback` vs `useMemo` Hooks

React Learning Guide

Boosting React Performance: useCallback vs. useMemo Hooks

React's useCallback and useMemo hooks are crucial for optimizing performance in your applications. Understanding when and how to use them can save you from unnecessary re-renders and ensure your app runs smoothly. In this article, we'll dive into real-world examples of using useCallback and useMemo effectively.

When to Use useCallback

The useCallback hook returns a memoized version of the callback function, which means it only recreates the function if one of its dependencies changes. This is particularly useful when passing functions as props to child components to prevent them from re-rendering unnecessarily.

Real-time Example: Preventing Unnecessary Re-renders

Suppose you have a parent component that passes a function to a child component. Without useCallback, the child component would re-render every time the parent component renders, even if the function logic hasn't changed.

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

const ChildComponent = memo(({ onIncrement }) => {
  console.log('Child component re-rendered');
  return <button onClick={onIncrement}>Increment</button>;
});

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

  const handleIncrement = useCallback(() => {
    setCount(count + 1);
  }, [count]);

  return (
    <div>
      <h1>Count: {count}</h1>
      <ChildComponent onIncrement={handleIncrement} />
    </div>
  );
};

export default ParentComponent;


Enter fullscreen mode Exit fullscreen mode

Explanation:

  • Memoizing the child component (memo(ChildComponent)): By wrapping ChildComponent in memo, it will now only re-render when its props change (in this case, when onIncrement changes).

  • useCallback: This ensures that the handleIncrement function does not change on every render, preventing the child from re-rendering unnecessarily due to prop changes.

When to Use useMemo

The useMemo hook is used to memoize the result of a function, recomputing the cached result only when one of its dependencies changes. It's useful for optimizing performance in situations where a function performs an expensive calculation.

Real-time Example: Optimizing Expensive Computations

Let's say you have a component that performs a computationally expensive operation, like filtering a large list.

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

const ExpensiveComponent = ({ items }) => {
  const [filter, setFilter] = useState('');

  // Using useMemo to optimize expensive filtering
  const filteredItems = useMemo(() => {
    console.log('Filtering items...');
    return items.filter(item => item.includes(filter));
  }, [items, filter]);

  return (
    <div>
      <input
        type="text"
        value={filter}
        onChange={(e) => setFilter(e.target.value)}
        placeholder="Filter items"
      />
      <ul>
        {filteredItems.map((item, index) => (
          <li key={index}>{item}</li>
        ))}
      </ul>
    </div>
  );
};

export default ExpensiveComponent;
Enter fullscreen mode Exit fullscreen mode

In this example, useMemo is used to cache the result of filtering the items array. This way, the expensive filtering operation is only recalculated when items or filter changes, avoiding unnecessary computations.

Guidelines for Using useCallback and useMemo

  • Use useCallback when passing functions to child components to avoid unnecessary re-renders.
  • Use useMemo for expensive computations that don't need to be recalculated on every render.
  • Avoid overusing them. Memoization adds complexity and can sometimes make code harder to read. Only use them when you identify a performance issue.
  • Remember the dependencies array. Always specify dependencies accurately; otherwise, you might run into bugs or unexpected behavior.

Conclusion

React's useCallback and useMemo hooks are powerful tools for optimizing component performance by avoiding unnecessary re-renders and expensive calculations. By carefully applying these hooks, you can ensure your React application runs efficiently.

Top comments (13)

Collapse
 
lwhiteley profile image
Layton Whiteley

Nice article.

One point of feedback. Examples in best practices articles can often be seen as good practices so ensure they are optimal as well.

const handleIncrement = useCallback(() => {
    setCount(count + 1);
  }, [count]);
Enter fullscreen mode Exit fullscreen mode

This code has a hidden bug as the state can be stale if a user is clicking fast

A better way to write this is

const handleIncrement = useCallback(() => {
    setCount(prevCount => prevCount + 1);
  }, []);
Enter fullscreen mode Exit fullscreen mode

Given that the example is trying to showcase the dependency array then maybe rework the example to use another dependency

Collapse
 
martinbaun profile image
Martin Baun

Great read! Looking forward to more like these!

Collapse
 
sivamani18 profile image
Sivasubramaniyam

I will be posting relevant topics to 'Mastering React' and adding appropriate links to each topic.

Collapse
 
elielson77 profile image
Elielson Melo

Thank you, I will try these tips

Collapse
 
ramk777stack profile image
Ramkumar.barani

Very informative, thanks for sharing.

Collapse
 
trisogene profile image
Daniel Dallimore Mallaby

This is not 100% correct, the child component will still rerender since his father state changed.

In order to prevent rerender of the child you will have to memoize the child ( export it with memo(ChildComponent)) so that this will only rerender if the props change.

Then as the article say you need to wrap function inside a usecallback so that when the father component rerender the function that will be passed as props wont change, thus the child component will not rerender.

Collapse
 
sivamani18 profile image
Sivasubramaniyam

Thank you for your feedback! 🙏

You're absolutely right. I should have included the step about memoizing the child component using React.memo to fully prevent it from re-rendering when the parent state changes.

This ensures that:

  • The ChildComponent only re-renders if its props change.
  • The useCallback hook prevents unnecessary re-creations of the handleIncrement function when the parent component re-renders.

Thanks again for pointing this out! 🙌

Collapse
 
lwhiteley profile image
Layton Whiteley • Edited

This rebuttal is not true.

If the props don't change then react will not rerender the child component even if the parent state changes.

Rerendering child components would be a massive performance hit for the tree if their props don't change

To prevent rerendering of child component you must first ensure it's props are stable meaning they only change when they need to.

If you need to further optimize when the component rerenders then you can use React.memo to exclude some props from consideration or change the default equality check that react uses which is Object.is

Object.is is a reference equality check

Adding React.memo on a component without first optimizing it's props will not have any effect

This can be easily tested by adding console logs in a component and changing the state in the parent and seeing if the child component renders a log. Or just use react dev tools.

This is all discussed on the react docs

react.dev/reference/react/memo

You should only rely on memo as a performance optimization. If your code doesn’t work without it, find the underlying problem and fix it first. Then you may add memo to improve performance.

Collapse
 
sivamani18 profile image
Sivasubramaniyam

The rebuttal you received is mostly correct when they argue that React won't re-render the child component if the props haven't changed. Indeed, React.memo should only be used after ensuring the props passed to the child are stable. However, useCallback plays a role in keeping the function reference stable—a common scenario that could otherwise lead to unwanted re-renders.

  • Stable props: Ensuring props are stable is the key point, and useCallback is one of the tools to achieve that for function props.
  • React.memo helps optimize the re-rendering process, but without stable props (like an unstable function reference), its benefits are limited.
Thread Thread
 
lwhiteley profile image
Layton Whiteley

However, useCallback plays a role in keeping the function reference stable

Yep that is my point

Collapse
 
wra-sol profile image
Nathaniel Arfin

useMemo and useCallback will be deprecated in React 19, FYI!

Collapse
 
sivamani18 profile image
Sivasubramaniyam

Thanks for the heads-up! 🙌

While it's good to stay informed about upcoming changes, a lot of companies are still running on older versions like React 16 or 17. In fact, some organizations take years before upgrading to the latest major versions due to stability concerns or the size of their codebase.

So, even though useMemo and useCallback might be deprecated in React 19, these hooks will likely still be widely used in production environments for quite a while. 😊

But it's always good to be aware of what's coming—thanks again for sharing! 🙏

Collapse
 
lwhiteley profile image
Layton Whiteley • Edited

I don't think they will be deprecated. The react compiler just automatically adds them for you.

That's not really a deprecation