⚡️ Boosting React Performance with Memo and useCallback
Hey everyone! 👋 In this blog, I’m excited to share my tips on optimizing performance in React. We've all experienced the frustration of unnecessary component re-renders, especially when a small change causes a cascade of unexpected errors. While React's reactivity is powerful, mastering its nuances can make a huge difference in maintaining smooth, error-free code. This blog will be your guide to achieving just that! 🚀
React Memo
In React, memo
is a hider-order function (a function that wraps another function) that helps prevent unnecessary component re-renders and keeps your app running smoothly. It's easy to use and ensures that a component doesn't re-render unless its props have actually changed. Let's look at an example to see how memo
works in action.
In this code, a change in the theme
prop causes a re-render of MainComponent
, subsequently re-rendering its child, MemoizedComponent
. This default behavior in React is similar to how your mother's anger affects you—it drains your energy, regardless of whether you did anything wrong.
However, React's memo
function comes to the rescue here. By wrapping MemoizedComponent
with memo
, even if MainComponent
re-renders due to a change in theme
, MemoizedComponent
won't re-render unless its actual props, like name
, change.
Without memo
, MemoizedComponent
would re-render every time its parent does, even if its props haven't changed. This makes memo
essential for optimizing performance in React apps.
Benefits
Using React's
memo
not only optimizes your application's performance but also prevents unnecessary re-rendering.Reduces complexity in debugging and maintaining code, as components re-render only when necessary.
Enhances responsiveness and smoothness of user interactions by minimizing re-rendering.
useCallback()
useCallback
also optimizes performance like memos but in a little different way since it deals with functions and dependencies. The basic syntax of useCallback
is const cachedFn = useCallback(fn, dependencies)
. It accepts two arguments, fn
, which is the function we want to cache and an array of dependencies
. When any value in dependencies
changes, React recreates the cached function. First, let us take a look at the code,
Here, the first step is declaring the increment
function using useCallback
at the beginning of the component. Hooks must be declared at the top level of a React component, not inside loops or conditionals. We pass a function to useCallback
that increments the value of count
using setCount()
.
Whenever the theme
prop of the component changes, it triggers a re-render of the component, executing the code again. Normally, this would recreate the increment
function and generate a new reference in memory. However, because we memoize the function with useCallback
and include count
as a dependency, React only recreates the function when the count
value changes.
For example, when you click the "Increment" button, it executes the code inside the increment
function and updates the state. The state update causes the component to re-render. This time, useCallback
checks if the dependencies array has changed—which it has, due to the updated count
—and recreates the function in memory.
This recreation happens only when you interact with the "Increment" button. Otherwise, the function maintains the same identity in memory whenever the component re-renders due to changes in the theme
prop.
Benefits
Prevents unnecessary recreation of functions in memory
Optimizes performance by caching functions
Maintains stable references to functions across renders.
Using memo and useContext together
Imagine you're passing a function as a prop to a child component, and you've used React.memo
to ensure the child component only re-renders when its props change. However, every time the parent component of this child re-renders, the function passed as a prop is recreated. This recreation causes the function reference to change, even though the code inside the function remains unchanged. As a result, React.memo
considers the function prop to have changed, triggering unnecessary re-renders of the child component.
In such cases, useCallback
proves invaluable. By wrapping the function in useCallback
, React memoizes the function instance. This memoization ensures that the function reference remains stable between renders unless its dependencies change. Therefore, even if the parent component re-renders, the function's reference stays the same as long as its dependencies remain unchanged. As a result, React.memo
correctly identifies that the function prop hasn't changed and prevents unnecessary re-renders of the child component.
An example of this code,
Here, the Button
component is memoized, and the increment
function is only recreated when the increment button is clicked. Thus, when the Component
re-renders due changes in the theme
prop, a new function won't be created (increment points to the same function as before) and memo
will prevent the Button
component from re-rendering as no change in prop is detected.
This approach not only optimizes performance by avoiding unnecessary renders but also ensures consistent behavior in scenarios where function props are passed to memoized child components.
And that wraps up our journey into optimizing React performance with memoization techniques. 🚀 I hope this guide proves invaluable in your quest for creating faster and more efficient React applications! Happy coding! 💻✨
Subscribe to my newsletter
Read articles from Aneesa Fatima directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by