All About UseCallback That You Need To Know

In this blog, we will understand the best way to use this hook with some examples:

As per the official react documentation, useCallBack is used to cache the function definition.

Syntax :

useCallback(callback function ()=>{}, dependencies [])

Example:

import { useCallback } from ‘react’;

export default function MediaDetail({ mediaId }) {
const handleSubmit = useCallback((mediaDetails) => {
post('/media/' + mediaId + '/add', {
mediaDetails,
});
}, [mediaId]);

–> On the initial render, useCallback returns the function you have passed.

–> During subsequent renders, it will return an already stored function from the last render if it gets the same value for provided dependencies else the function you have passed during the current render.

–> React uses object.is() to check if the value of dependencies got changed or not.

–> As this is a hook so it can only be called from the top level of the component and should not be called inside any loop or condition.

Many developers are not able to perform different operations within useCallBack:

1) Updating The State Within UseCallback Hook:

There are some scenarios where the developer needs to update the state based on the previous state from a cached callback function.

Here, handleAddToCartItems function specifies cartItems as a dependency because it computes the next cartItems from it:

function Cart() {
const [cartItems, setCartItems] = useState([]);

const handleAddToCartItems = useCallback((_item) => {
setCartItems([…cartItems, _item]);
}, [cartItems]);
//…
}

You’ll usually want memoized functions to have as few dependencies as possible. When you read some state only to calculate the next state, you can remove that dependency by passing an updater function instead as follow:

function Cart() {
const [cartItems, setCartItems] = useState([]);
const handleAddToCartItems = useCallback((_item) => {
setCartItems(cartItems => […cartItems, _item]); // use of updater function
}, []); // no dependency required
// …
}
2) Using Memo & UseCallback Together:

function ShippingPage({ productId, theme }) {

// Every time the theme changes, this will be a different function…
function handleSubmit(orderDetails) {
post('/product/' + productId + '/buy', {
orderDetails,
});
}
return (
{/* … so ShippingForm's props will never be the same, and it will re-render every time */}
);
}

What will be the optimized version of this code?

Most of the time developers think of wrapping up the child component in memo

import { memo } from 'react';
const ShippingForm = memo(function ShippingForm({ onSubmit }) {
// …
});

But as the props of this component will always be different so memo won’t be able to optimize the render cycles of this component.

So, the next step is to make sure the props are not being updated unnecessarily. How can we do that?

Let’s think of using useCallback for the function definition which is being passed as props to the child component

const handleSubmit = useCallback((orderDetails) => { // Informing React to cache this function between re-renders…
post('/product/' + productId + '/buy', {
orderDetails,
});
}, [productId]); // …so as long as these dependencies don't change…

As soon as you move your props function definition to useCallback , the child component will stop re-rendering until the value of useCallback dependencies gets changed.

This is how we can use memo & useCallback together.

3) Optimize Your Custom Hook:

React recommends wrapping any functions that custom hooks return into useCallback

This is because the component which is using this custom hook can be optimized efficiently as the custom hook will not return the new definition of the function on every render cycle until the value of dependencies is not changed.

4) Prevent UseEffect From Triggering The Callback Function When A Function Is Added As A Dependency
useEffect(() => {
const config = getConfigData();
// …
}, [getConfigData]); // This dependency changes on every render

The above useEffect will get triggered on every render cycle as it will get a new definition of getConfigData function

Let’s optimize it using useCallback:

const getConfigData = useCallback(() => {
return {
//…
};
}, [region]); // Only changes when region (dependency) changes
useEffect(() => {
const config = getConfigData();
// …
}, [getConfigData]);// Only changes when getConfigData changes
5) Using UseCallback For Each Component Rendered In A Loop
{items.map(item => {
// We can not call useCallback in a loop like this:
const handleClick = useCallback(() => {
sendReport(id)
}, [item]);
return (
      <div key={item.id}>
        <chart onClick={handleClick} />
      </div>
    );
  })
}

Let’s optimize this:

React recommends that you can’t call useCallback hook inside loops or conditions. If you need to do that, extract a new component and move the state into it.

1) We can create a separate child component that will render the chart UI & move corresponding handlers to the chart component with useCallback.

function Report({ item }) {
// Call useCallback at the top level:
const handleClick = useCallback(() => {
sendReport(item)
}, [item]);
return (
// this will re-render only when handleClick changes
);
}

2) We can also use the memo instead of useCallBack which in turn skips all render cycles of the Report component for which values of props are the same.

Hope You Have Learned Some New Techniques To Use UseCallback Wisely. Check Our Our Other Blogs For Similar Topics.


Posted

in

by

Tags:

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *