Use the useCallback
hook when passing functions as props to prevent unnecessary re-renders of child components.
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 anduseCallback
anduseMemo
hooks to optimize functions and computed values within components.
memo
functionReact’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.
useCallback
hookTo 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.
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.
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.
useMemo
for value memoizationThe 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.
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.
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.
Haven’t found what you were looking for? Contact Us
Free Resources