Use the useCallback hook when passing functions as props to prevent unnecessary re-renders of child components.
Performance optimization hooks in React
Key Takeaways
React’s
useCallbackanduseMemohooks 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
memofunction 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
useCallbackanduseMemohooks 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
useCallbackhook 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
useMemohook 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
memofunction to memoize components anduseCallbackanduseMemohooks 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
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)
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—
useCallbackanduseState, and another component calledChild.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,
memoizedCallbackusing theuseCallbackhook, 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 thechangeChildNumberand 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 thememoizedCallbackwill always be the same function in the memory.Lines 12–14: A local function setter calls the
setLocalNumberwhich is updating the state at each press by1.Lines 16–18: A local function that calls the
setChildNumberwhich is called in the callback.Lines 20–29: Here, we are rendering the
Childcomponent which is expecting thechildNumberto not change its value until any change is made in theChildcomponent. 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 thelocalNumber.
In the Child.js file, we have the following:
Lines 4–7: We have function,
test, that should call its parent componentschangeNumberfunction, 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
testfunction, 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);
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
useStateanduseMemohooks.Lines 4–7: We define three state variables using the
useStatehook:var1and var2store individual counter values.arrholds an array of numbers, and thesetArrValfunction allows us to update the array state.
Line 8: We use the
useMemohook to memoize the result of thegetMaximum()function.Lines 10–18: We define two increment functions:
incrementVar1increasesvar1by1.incrementVar2increasesvar2by1.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 thearrarray usingMath.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 thearrdependency changes, theuseMemohook will recompute the maximum value.Lines 30–43: We render the component UI. The two buttons increment
var1andvar2. We also create a third button that changes the array state usingchangeArr(). 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?
Can I use useEffect with useCallback or useMemo?
What’s the main difference between useMemo and useCallback?
Free Resources