Performance optimization hooks in React

Key Takeaways

  • React’s useCallback and useMemo hooks help optimize performance by reducing unnecessary re-renders. As React applications grow in size, unoptimized components may lead to slow user interfaces. Proper usage of these performance hooks ensures that React applications remain fast and responsive for an optimal user experience.

  • Wrapping components with React’s memo function ensures that a component only re-renders when its props change. This is an effective way to optimize child components that rely on stable props.

  • Memoization is the process of caching the results of functions or computations to reuse them when needed. The useCallback and useMemo hooks leverage memoization to improve the efficiency of function and value management by avoiding recalculating the same values on every render.

  • When functions are passed as props to child components, React usually re-renders the child component on every parent render. The useCallback hook caches these functions so that React does not unnecessarily re-create the same function, ensuring that the child component only re-renders when needed.

  • If an application performs intensive calculations, such as sorting, filtering, or searching within large datasets, the useMemo hook ensures that these computations only run when required. This reduces the computational overhead and keeps the UI responsive, as React uses the cached value until dependencies change.

Developers who want to ensure that users have a positive user experience and keep them engaged in the application need to focus on improving application performance. React applications have a very fast UI by default, however, when the size of the application increases there might be performance issues.

In React, every time a component re-renders, all functions declared within that component are re-created. This behavior ensures that the component logic reflects the latest state, but it can lead to performance issues if the component is large or if certain functions are passed as props to child components. React does not re-render individual functions within a component. Instead, the entire component is re-rendered whenever its state or props change.

If a component has many functions, React recreates all of them on each render, even if some are not directly affected by the changes. This can make the application feel slower, especially when these functions are passed down to other components as props, causing child components to re-render unnecessarily. To avoid this scenario, we use the concept of memoization.

Note: Memoization is not exclusive to React, but React offers tools to leverage this concept for performance optimization. It provides the memo function to memoize components and useCallback and useMemo hooks to optimize functions and computed values within components.

React’s memo function

React’s memo function is used to memoize functional components. It prevents unnecessary re-renders by comparing the current and previous props through a shallow comparison. If the props are the same, React skips the re-render. However, if any prop has changed, the component will re-render to ensure the latest data is displayed.

Note: Memoization is not free, you are trading space for time.

Now, what if we have separate functions that we are passing down as props to some other component? They will not be memoized by the memo function and will re-render every single time. This is because functions are re-created with a new reference on every render, making them appear as different objects to React.

Optimizing functions with the useCallback hook

To understand the use of the useCallback hook, you should have a slight understanding of the concept of referential integrityReferential integrity is when we create two variables and compare them with each other and the answer would be true because they are just comparing the values and the data type. In the case of objects, when we create two objects and compare them, the result would be false as the objects are pointing to two different memory blocks. So, when a slight change is made every time, it creates a new object that has a new memory address— breaking the referential integrity when the shallow comparison of props is made. Hence, we have to re-render the whole component again..

When functions are passed as props to child components, React may re-render the child unnecessarily, even if the logic of the function hasn’t changed. This is where useCallback comes in. The useCallback hook memoizes a function, ensuring that it maintains the same reference across renders unless its dependencies change. This prevents React from triggering re-renders in child components that receive the function as a prop.

The syntax to define it is as follows:

const function_to_cache = useCallback(fn, dependencies)
useCallback hook syntax

The useCallback function has the following parameters:

  • fn: The callback function to be memoized between the re-renders.

  • dependencies: Values that determine when the function should be re-created.

When we render our component first, useCallback will return the function passed to it. After every next render, it will compare the dependencies with the dependencies passed during the last render. If no dependency changes after comparison, it will return the old function. Now, throughout the lifetime of this component, the memoized callback function will always be the same function in the memory. We are never actually creating a new function but keeping this function over and over again at each render, even by different values. The function will stay referentially the same.

Explore React hooks by implementing them in a real world use case in this project, Build a Task Manager Using React.

Let’s look into an example of this to have a better understanding.

Output

Clicking the "Click to increment local" button increments the localNumber state in the App component. This means that only the App component re-renders, and you will not see "Child is rendering" in the console because the Child component does not re-render.

Clicking the "Click to change child value" button inside the Child component, triggers the test() function, which calls the memoized changeNumber function with a random number. The Child component re-renders, and "Child is rendering" is logged to the console.

Explanation

In the App.js file, we have the following:

  • Lines 1–2: We import the React hooks—useCallback and useState, and another component called Child.

  • Lines 5–6: We are defining two state variables and functions as props to be passed down.

  • Lines 8–10: We define a memoized function, memoizedCallback using the useCallback hook, which accepts two arguments and then returns a new function. The first argument is the actual function that we want to memoize. Here, it is the changeChildNumber and then we will pass the number that we received in this function. The second argument is an empty dependency array because we don’t want this to change. But the point here is that the memoizedCallback will always be the same function in the memory.

  • Lines 12–14: A local function setter calls the setLocalNumber which is updating the state at each press by 1.

  • Lines 16–18: A local function that calls the setChildNumber which is called in the callback.

  • Lines 20–29: Here, we are rendering the Child component which is expecting the childNumber to not change its value until any change is made in the Child component. We get this by the memoized function that we have created above. Then we have a button that, on click, will change the value of the localNumber.

In the Child.js file, we have the following:

  • Lines 4–7: We have function, test, that should call its parent components changeNumber function, which is being passed via props.

    • Line 5: We call the Math.random() function, that generates a random number. Every time this function is called, it will change the props and render the child component.

    • Line 6: We print a statement on the console when the child is rendered.

  • Lines 10–13: We print the number that has been changed. The button will call the test function, whenever it is clicked.

Practice using hooks in a component with this project, Build an Image Sharing App with MERN Stack.

Using the useMemo for value memoization

The useMemo hook is used to cache the result of expensive computations to avoid recalculating them on every render. It is especially useful when dealing with values, large datasets, sorting algorithms, or filtering operations.

The syntax to define it is as follows:

const value_to_cache = useMemo(computeValue, dependencies);
useMemo hook syntax

The useMemo function has the following parameters:

  • computeValue: The callback function that contains the computation logic and returns a value of any type.

  • dependencies: Values that the computation relies on.

Let’s look into an example of this to have a better understanding.

Output

When you click the "Count Var1" or "Count Var2" buttons, the respective counters increment, and you will see the corresponding log ("Increasing Var1" or "Increasing Var2") in the console. The getMaximum() function is not called because the array has not changed, meaning React reuses the memoized value.

Now, clicking "Change Array value" triggers the setArrVal function, updating the array to [60, 88, 98, 23]. The largest number is recalculated, and "Returning max" is logged in the console. The new largest number, 98 is displayed in the UI.

Without useMemo, the Math.max() function would run every time the component renders, even when the counters (var1 and var2) are updated. With useMemo, React only recomputes the maximum value when the array changes, ensuring better performance.

Learn more about hooks, by trying this project, Build a Location Tracker Using Leaflet, ReactJS, and Express.js.

Explanation

  • Lines 1–2: We import the useState and useMemo hooks.

  • Lines 4–7: We define three state variables using the useState hook:

    • var1 and var2 store individual counter values.

    • arr holds an array of numbers, and the setArrVal function allows us to update the array state.

  • Line 8: We use the useMemo hook to memoize the result of the getMaximum() function.

  • Lines 10–18: We define two increment functions:

    • incrementVar1 increases var1 by 1.

    • incrementVar2 increases var2 by 1.

    • These functions only update their respective state values and do not affect the memoized value.

  • Lines 20-23: We define the getMaximum() function that computes the largest number in the arr array using Math.max(). Every time the array changes, this function will log "Returning max" to the console.

  • Lines 25-27: We define the changeArr() function that updates the array state to a new set of numbers. Since the arr dependency changes, the useMemo hook will recompute the maximum value.

  • Lines 30–43: We render the component UI. The two buttons increment var1 and var2. We also create a third button that changes the array state using changeArr(). The largest number from the memoized value is displayed. If the array is changed, the largest number is recomputed and updated in the UI.

Frequently asked questions

Haven’t found what you were looking for? Contact Us


When should I use useCallback in React?

Use the useCallback hook when passing functions as props to prevent unnecessary re-renders of child components.


Can I use useEffect with useCallback or useMemo?

Yes! You can use useEffect hook alongside memoization hooks to run side effects based on memoized values or functions.


What’s the main difference between useMemo and useCallback?

The main difference between these two hooks is that the useMemo hook caches values while the useCallback hook caches functions.


Free Resources

Copyright ©2025 Educative, Inc. All rights reserved