Learning How to Tame React’s useCallback Hook

It’s no secret that React.js has become widely popular in recent years. It’s now the JavaScript library of choice for many of the internet’s biggest players, including Facebook and WhatsApp.

One of the main reasons for its rise was the introduction of hooks in version 16.8. React hooks allow you to tap into React functionality without having to write class components. Now functional components with hooks have become developers’ go-to structure for working with React.

In this blog post, we’ll dig deeper into one specific hook — useCallback — because it touches on a very fundamental part of functional programming known as memoization.

By the end of this article, you’ll know exactly how and when to utilize the useCallback hook, as well as how to make the best of its performance-enhancing capabilities.

Ready? Let’s dive in!


What Is Memoization?

Memoization is when a complex function stores its output so the next time it is called with the same input, it can skip any of the complex computations and simply return to you the output it has already calculated.

This can have a big effect on memory allocation and performance, and that strain is what the useCallback hook is meant to alleviate.

React’s useCallback vs useMemo

At this point, it’s worth mentioning that useCallback pairs really nicely with another hook called useMemo. We’ll discuss them both, but in this piece, we’re going to focus on useCallback as the main topic.

The key difference is that useMemo returns a memoized value, whereas useCallback returns a memoized function. That means that useMemo is used for storing a computed value, while useCallback returns a function that you can call later on.

Both of these hooks will give you back a cached version unless one of their dependencies (e.g. state or props) changes.

Let’s take a look at the two functions in action:

import { useMemo, useCallback } from 'react'
const values = [3, 9, 6, 4, 2, 1]

// This will always return the same value, a sorted array. Once the values array changes then this will recompute.
const memoizedValue = useMemo(() => values.sort(), [values])

// This will give me back a function that can be called later on. It will always return the same result unless the values array is modified.
const memoizedFunction = useCallback(() => values.sort(), [values])

The code snippet above is a contrived example but shows the difference between the two callbacks:

  1. memoizedValue will become the array [1, 2, 3, 4, 6, 9]. As long as the values variable stays so will memoizedValue and will never recompute.
  2. memoizedFunction will be a function that when called will return the array [1, 2, 3, 4, 6, 9].

What’s great about these two callbacks is they become cached and hang around until the dependency array changes. This means that on a render, they won’t get garbage collected.

React.js is now the JavaScript library of choice for many of the internet's biggest players, including Facebook and WhatsApp. ? Learn more in this guide ⬇Click to Tweet

Rendering and React

Why is memoization important when it comes to React?

It has to do with how React renders your components. React uses a Virtual DOM stored in memory to compare data and decide what to update.

The virtual DOM is what helps React with performance and keeps your application fast. By default, if any value in your component changes, the entire component will re-render. This is what makes React “reactive” to user input and allows for the screen to update without having to reload the page.

That said, there may be times you don’t want to render your component because changes won’t affect that component. This is where memoization through useCallback and useMemo comes in handy.

When React re-renders your component, it also recreates the functions you’ve declared inside your component.

Note that when comparing the equality of a function to another function, they will always be false. Because a function is also an object, it will only equal itself:

// these variables contain the exact same function but they are not equal
const hello = () => console.log('Hello Matt')
const hello2 = () => console.log('Hello Matt')

hello === hello2 // false
hello === hello // true

In other words, when React re-renders your component, it will see any functions that are declared in your component as being new functions.

This is fine most of the time, and simple functions are easy to compute and will not impact performance. But the other times when you don’t want the function to be seen as a new function you can rely on useCallback to help you out.

You might be thinking, “When would I not want a function to be seen as a new function?” Well, there are certain cases when useCallback makes more sense:

  1. You’re passing the function to another component that is also memoized (useMemo)
  2. Your function has an internal state it needs to remember
  3. Your function is a dependency of another hook, like useEffect for example

Performance Benefits of React useCallback

When useCallback is properly used, it can help to 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:

A colorful bar graph comparing the overall transaction time of PHP, MySQL, Reddis, and external (other) in milliseconds.
Bar graph generated using a React component

If the parent component for your data visualization’s component re-renders, but the props or state that changed do not affect that component, then 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 users bandwidth and provide them with a smoother user experience.

Struggling with downtime and WordPress problems? Kinsta is the hosting solution designed to save you time! Check out our features

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, just be sure the performance benefits will outweigh the drawbacks.

Example

Below is a simple setup with a Button component and Counter component. The Counter has two pieces of state and renders out two Button components, each that will update a separate piece 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

This is because 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).

Ready to learn exactly how and when to utilize the useCallback hook, as well as how to make the best of its performance-enhancing capabilities? ? Get started here! ?Click to Tweet

Summary

As cool as useCallback and useMemo are, remember that they have specific use cases — you should not be wrapping every function with these hooks. If the function is computationally complex, a dependency of another hook or a prop passed to a memoized component are good indicators that you might want to reach for useCallback.

We hope this has helped you understand this advanced functionality in React, and that you perhaps gained more confidence with functional programming along the way!

The post Learning How to Tame React’s useCallback Hook appeared first on Kinsta®.

版权声明:
作者:zhangchen
链接:https://www.techfm.club/p/30403.html
来源:TechFM
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
< <上一篇
下一篇>>