In this tutorial, we’ll learn, React useCallback hook in detail with examples. In React App, change in any state in a Rect component causes Rerendering of that component, sometimes there is unnecessary rendering and unnecessary calling function. React framework has tools to avoid unnecessary rendering and improve performance on expensive components, we can use the following tools from React.
- useCallback
- useMemo
- memo
In most cases, we don’t need these optimizations, all these performance strategies come with costs. Use of these hooks, we need add extra code and extra comparison on each render. It best practice to first calculate the cost of using these approaches and you need a clear reason for using them, otherwise, you might be doing more harm than good.
What is React useCallback ?
In React we have two hooks, for improving performance and avoiding unnecessary re-rendering of components. The functionality of useMemo and useCallback are different.
The React useCallback Hook returns a memoized version of the callback function that only changes one of its dependencies has changed. We can use useCallback in React Function component. This allows us to prevent the allocation of resources for intensive functions that will not call on every render.
Syntax of use useCallback
useCallback(fn, []) // fn is callbackfunction
const memoizedFunction = useCallback(
() => {
//
},
[variable1, variable2 .... variablen],
);
Where fn is a callback function in which we want to memorize, and second is dependencies array like we do have on useEffect hooks. The useCallback hook only runs when one of its dependencies is changed. The useCallback is useful when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders (e.g. shouldCooumponentUpdate).
In Javascript or in a general programming language, we have Referential equality for comparing variables, functions, objects,s, and other data types. In referential equality we have
- Comparison by value: This applies to primitive data, where we compare data by its value.
- Comparison by reference: We compare nonprimitive variables like(Array, object, and function) in Javascript using its memory location, instead its value.
const num1 = 10;
let num2 = 10;
//Comparision by value on Primitive type
num1 === num2 // True
'Hello' === 'Hello' // True
'Hello' === 'hello' //False
true === true //True
// Comparision by reference
[] === [] //Array are not same False
{ name:'edupala.com', since: '2016'} === { name:'edupala.com', since: '2016'} // False not same
In React whenever a function component rerenders, it creates a new function or function component, React creates a new referential identity every time a component rerender and the old one gets garbage collected means remove from memory. Let’s quoted it again,
The useCallback is useful when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders.
React documentation
The useCallback will return a function that maintains its referential identity as long as the dependencies don’t change.
I was stumbling on usCallback hook, I found this article very helpful When to useMemo and useCallback hook it talks about why is worse to use these performance hooks sometime. First, we must calculate the cost of using these performance hooks, sometimes using them might be doing more harm than good.
React useCallback Example
Let’s demonstrate with an example of React useCallback example, let’s first implement an example without useCallback hook, so that we can see the problem of rendering of child component when any state changes in its parent component. Let’s create our the useCallback example project.
npx create-react-app callback-app
cd callback-app
Here is a screenshot of the unnecessary re-rendering of the Child component, when any of the parent states change, even the Child component state didn’t change.
We have two components, where the App component is the parent component, and we have a child component called the RandomNumber. The App component has two state numbers and randomArray. The app component also has two functions, onIncrement and addNewRandom function.
Clicking on the Increment button will invoke the onIncrement function, which increments the value of a number. In the onIncrement function, we set a new increment number to a number state, thus change in a component state causes the re-rendering of a component including its child component.
The addNewRandomNumber function will add a new random number to existing an random array.
On the above screenshot, we can see that clicking on the Parent increment button also causes rerendering of the child component, even if the child component has the same state and this is unnecessary re-rendering of a component.
Here is the code of the App.js component file
import React, { useState, useCallback } from "react";
import RandomNumber from "./components/RandomNumber";
import "./App.css";
function App() {
const [number, setNumber] = useState(1);
const [randomArray, setRandomArray] = useState([]);
console.log("Parent App component rerender");
const onIncrement = () => {
setNumber((prev) => prev + 1);
};
const addNewRandomNumber = () => {
setRandomArray([...randomArray, Math.floor(Math.random() * 100)]);
};
return (
<div className="app-container">
<div>Parent component : Increment Number value {number}</div>
<button onClick={onIncrement}>Increment</button>
<hr />
<RandomNumber randomArray={randomArray} addNewNumber={addNewRandomNumber} />
</div>
);
}
export default App;
The RandomNumber component receives a random number array as props and also addNewNumber function as props also. We used this child component to display the list of all numbers in the random array, and here is the code of components/RandomNumber.jsx
import { memo } from "react";
const RandomNumber = ({ randomArray, addNewNumber }) => {
console.log('RandomNumber child Component render');
return (
<>
<h2>Random numbers in Child component</h2>
{randomArray.map((number, index) => {
return <span key={index}>{number},</span>;
})}
<button onClick={addNewNumber}>Add new random number</button>
</>
);
};
export default memo(RandomNumber);
Clicking this Add new random number will invoke addNewNumber function which we got as props from its parent component App.js file and this will add a new random number to our existing randomArray variable.
Solution for rendering component: useCallback hook
Because of the above re-rendering issues, we can use useCallback hook to prevent unneeded re-renders of child components thus can help to optimize the performance of your React application. Here is a screenshot of React useCallback example.
In the above example screenshot, we can see that clicking on the Increment button will only rerender its parent component because of a change in the state but the child component is not rendered.
All other codes are the same as before except the parent App.js component, let’s add useCallback hook on addNewRandomNumber function so that we prevent the Child component from rendering.
import React, { useState, useCallback } from "react";
import RandomNumber from "./components/RandomNumber";
import "./App.css";
function App() {
const [number, setNumber] = useState(1);
const [randomArray, setRandomArray] = useState([]);
console.log("Parent App component rerender");
const onIncrement = () => {
setNumber((prev) => prev + 1);
};
const addNewRandomNumber = useCallback(() => {
setRandomArray([...randomArray, Math.floor(Math.random() * 100)]);
}, [randomArray]);
return (
<div className="app-container">
<div>Parent component : Increment Number value {number}</div>
<button onClick={onIncrement}>Increment</button>
<hr />
<RandomNumber
randomArray={randomArray}
addNewNumber={addNewRandomNumber}
/>
</div>
);
}
export default App;
In the bold code where we wrapped function using useCallback, only when randomArray value is changed, which we pass as props to Child component, we rerender the Child component, because of useCallback dependency on randomArray.
Conclusion
We had learned the React useCallback hook to improve performance and most of the time we may not need it. Before using it, we first calculate the cost and benefit of using it.
Related Post
- How to implement React table and its component?
- When and how to use React Context API?
- React Axios for HTTP method with REST API
- React router navigate outside component.
- How to implement React routing using react-router-dom?