react js-tutorial

React useCallback Hook

The useCallback hook is used when a component’s child is rendering it repeatedly without a reason.

Pass an array of dependencies together with an inline callback. A memoized version of the callback that only changes if one of the dependencies has changed is what useCallback returns. This is helpful for sending callbacks to optimised child components that don’t need to render everything because they rely on reference equality.

Performance Benefits of React useCallback

When useCallback is appropriately used, it can help speed up your application and prevent components from re-rendering if they don’t need to.

Let’s say, for example, you have a component that fetches a large amount of data and is responsible for displaying that data in the form of a chart or a graph, like this:

Suppose the parent component for your data visualization’s component re-renders, but the changed props or state do not affect that component. In that case, you probably don’t want or need to re-render it and refetch all the data. Avoiding this re-render and refetch can save your user’s bandwidth and provide a smoother user experience.

Drawbacks of React useCallback

Although this hook can help you improve performance, it also comes with its pitfalls. Some things to consider before using useCallback (and useMemo) are:

  • Garbage collection: The other functions that are not already memoized will get thrown away by React to free up memory.
  • Memory allocation: Similar to garbage collection, the more memoized functions you have, the more memory that’ll be required. Plus, each time you use these callbacks, there’s a bunch of code inside React that needs to use even more memory to provide you with the cached output.
  • Code complexity: When you start wrapping functions in these hooks, you immediately increase the complexity of your code. It now requires more understanding of why these hooks are being used and confirmation that they’re used correctly.

Being aware of the above pitfalls can save you the headache of stumbling across them yourself. When considering employing useCallback, be sure the performance benefits will outweigh the drawbacks.

React useCallback Example

Below is a simple setup with a Button component and a Counter component. The Counter has two pieces of state and renders out two Button components, each that will update a separate part of the Counter components state.

The Button component takes in two props: handleClick and name. Each time the Button is rendered, it will log to the console.

import { useCallback, useState } from 'react'

const Button = ({handleClick, name}) => {
  console.log(`${name} rendered`)
  return <button onClick={handleClick}>{name}</button>
}

const Counter = () => {

console.log('counter rendered')
  const [countOne, setCountOne] = useState(0)
  const [countTwo, setCountTwo] = useState(0)
  return (
    <>
      {countOne} {countTwo}
      <Button handleClick={() => setCountOne(countOne + 1)} name="button1" />
      <Button handleClick={() => setCountTwo(countTwo + 1)} name="button1" />
    </>
  )
}

In this example, whenever you click on either button, you’ll see this in the console:

// counter rendered

// button1 rendered
// button2 rendered

Now, if we apply useCallback to our handleClick functions and wrap our Button in React.memo, we can see what useCallback provides us. React.memo is similar to useMemo and allows us to memoize a component.

import { useCallback, useState } from 'react'

const Button = React.memo(({handleClick, name}) => {
  console.log(`${name} rendered`)
  return <button onClick={handleClick}>{name}</button>
})

const Counter = () => {
  console.log('counter rendered')
  const [countOne, setCountOne] = useState(0)
  const [countTwo, setCountTwo] = useState(0)
  const memoizedSetCountOne = useCallback(() => setCountOne(countOne + 1), [countOne)
  const memoizedSetCountTwo = useCallback(() => setCountTwo(countTwo + 1), [countTwo])
  return (
    <>
        {countOne} {countTwo}
        <Button handleClick={memoizedSetCountOne} name="button1" />
        <Button handleClick={memoizedSetCountTwo} name="button1" />
    </>
  )
}

Now when we click either of the buttons, we’ll only see the button we clicked to log into the console:

// counter rendered

// button1 rendered

// counter rendered

// button2 rendered

We’ve applied memoization to our button component, and the prop values that are passed to it are seen as equal. The two handleClick functions are cached and will be seen as the same function by React until the value of an item in the dependency array changes (e.g. countOne, countTwo).

RECOMMENDED ARTICLES





Leave a Reply

Your email address will not be published.