React Lifecycle Method in Functional Component (Part II)
useCallback()
useCallback hook will return a memoized version of the callback function , it only updates when the associated dependencies are updated . Memoization is a way to cache a result so that it doesn’t need to be computed again.
const cachedFn = useCallback(fn, dependencies)
useCallback hook takes two parameter , first a function that needs to be memoized , it is returned by the hook not called by the hook. Second list of all the values that are used inside the function that eg. state,props, variable and function used inside the function body.
Let’s create a render a list component and optimize the code using useCallback and useMemo hooks.
Create a simple Component and call it inside your App.js
import React, { useState, useCallback } from "react";
const ListItem = ({ item, handleStatusChange }) => {
return (
<div
style={{
padding: "10px",
margin: "10px",
border: "1px solid black",
background: item?.isActive ? "green" : "red",
}}
>
{console.log("here--rendered list item", item.name)}
<div>{item?.name}</div>
<div onClick={() => handleStatusChange(item)}>
Status:{item?.isActive ? "Active" : "InActive"}
</div>
</div>
);
};
// Our Main Component
const Testing = () => {
const [list, setList] = useState([
{
name: "Goku",
isActive: true,
},
{
name: "Naruto",
isActive: false,
},
{
name: "Luffy",
isActive: false,
},
{
name: "Ichigo",
isActive: false,
},
]);
const handleStatusChange = (obj) => {
setList((prevList) => {
const lst = [...prevList].map((item) => {
if (item?.name === obj?.name) {
return { ...item, isActive: !obj.isActive };
} else {
return item;
}
});
return lst;
});
};
return (
<div>
{list?.map((item) => {
return (
<ListItem
item={item}
handleStatusChange={handleStatusChange}
key={item.name}
/>
);
})}
</div>
);
};
export default Testing;
As You can see in above images, when the status of a single item is changed of an item all the items are re-rendered, to avoid re-render of other items when only a specific item is being updated we pass the handleStatusChange as callback function inside useCallback hook ,although the list Items will still re-render because the parent component will be re-rendered so the child component will also re-render to prevent this, we wrap the ListItem component in memo, React.memo is a Higher Order Component that prevents a functional component from being re-rendered if its props or state do not change.
Note : useCallback will be used if not every time parent re-renders the function is also created again ,because of referential equality it will be different from previously created function, as it is passed in the ListItme it will re-render the Item even you memoize the ListItem because the function is different in the ListItem
import React, { useState, useCallback } from "react";
const ListItem = React.memo(({ item, handleStatusChange }) => {
return (
<div
style={{
padding: "10px",
margin: "10px",
border: "1px solid black",
background: item?.isActive ? "green" : "red",
}}
>
{console.log("here--rendered list item", item.name)}
<div>{item?.name}</div>
<div onClick={() => handleStatusChange(item)}>
Status:{item?.isActive ? "Active" : "InActive"}
</div>
</div>
);
});
const Testing = () => {
const [list, setList] = useState([
{
name: "Goku",
isActive: true,
},
{
name: "Naruto",
isActive: false,
},
{
name: "Luffy",
isActive: false,
},
{
name: "Ichigo",
isActive: false,
},
]);
//callback function
const handleStatusChange = useCallback((obj) => {
setList((prevList) => {
const lst = [...prevList].map((item) => {
if (item?.name === obj?.name) {
return { ...item, isActive: !obj.isActive };
} else {
return item;
}
});
return lst;
});
}, []);
return (
<div>
{list?.map((item) => {
return (
<ListItem
item={item}
handleStatusChange={handleStatusChange}
key={item.name}
/>
);
})}
</div>
);
};
export default Testing;
As you can see in above images only the item whose status is being changed is re-rendered now ,instead of entire list.
useMemo()
useMemo hook is used to memoized the value returned by the function when called.
const cachedValue = useMemo(calculateValue, dependencies)
it accepts function and dependencies as an argument and returns the value returned by the function. It will call the function while initial rendering then and return the value while caching it when the component is re-rendered again if the dependency is changes the function will be called again else the stored value will be returned.
useMemo is most commonly used to reduce re-rendering of the component and avoid expensive re-calculations.
As ,you can see in above useCallback example to avoid re-rendering we use memo to avoid re-rendering of the component when the item is not being updated.
Here’s an example of a memo function how it works internally
let storedVals = {};
const multiply = (item) => item * 10;
function customMemo(item, storedVals) {
if (!!storedVals[item]) {
return storedVals[item];
} else {
const res = multiply(item);
storedVals[item] = res;
return res;
}
}
Here we store the result for each value when the function is called , if the value is present it id returned and re-calculation is avoided and if not the function is called and the value is cached and returned .
If you’d like to be in the know, subscribe, clap, like and share. Cheers!