DEV Community

Haruto Hirakawa
Haruto Hirakawa

Posted on

Properly understand Hooks to improve your application performance

When developing a product with React, if you write components and use Hooks a lot without paying special attention, the overall operation may become slow, even if the project size is not large.

It's important to understand the role of Hooks and to use them in the right places. In this article, we summarize the explanations of some of the Hooks provided by React.

📌 useMemo

useMemo is a hook to save (memoize) the result of a function and retrieve the value when it is called again. This eliminates unnecessary recalculation and improves performance.

const value = useMemo(() => {
  // doSomething
}, [])
Enter fullscreen mode Exit fullscreen mode

It's effective when used for functions with high computational cost. Also, unlike useCallback, it is not a Hook that memoizes the function itself. Also, by passing variables as the second argument (deps), the function can be called to recalculate when thosae values are changed.

👎 The following code calls a function each time a component is rendered.

const Component = () => {
  const doSomething = () => {
    // A function with a with high computational cost
  }

  return (
    <div>{doSomething()}</div>
  )
}
Enter fullscreen mode Exit fullscreen mode

👍 The following code calls the function only when the component is rendered for the first time or when the value passed to deps is updated.

import { useMemo } from 'react'

const Component = () => {
  const doSomething = useMemo(() => {
    // A function with a with high computational cost
  }, [])

  return (
    <div>{doSomething}</div>
  )
}
Enter fullscreen mode Exit fullscreen mode

📌 useCallback

useCallback memoizes the function itself. It is mainly used for functions defined inside components such as handlers.

All functions defined inside a component are regenerated when the component is re-rendered (e.g., when the props value is updated or the parent component is re-rendered).

Basically, there is no need to regenerate functions on every rendering pass, so the use of useCallback improves performance.

⚡ Example

For example, suppose you have the following code In this code, re-rendering of the Child component will occur at the following times:

  • When setValue is called by some action
  • When someData is updated by some action

Assuming that this action can occur frequently, each time this happens, the old function is discarded and a new function is created and bound to the button. The accumulation of such components will needlessly degrade performance.

const Parent = () => {
  const [value, setValue] = useState(0)

  return (
    <>
      <div>{value}</div>
      <Child data={someData} />
    </>
  )
}

const Child = (props) => {
  const handleClick = () => {
    // doSomething
  }

  return (
    <>
      <div>{props.data}</div>
      <button onClick={handleClick}></button>
    </>
  )
}
Enter fullscreen mode Exit fullscreen mode

In this case, you can wrap the handleClick function with useCallback.

import { useCallback } from 'react'

// ...

const Child = (props) => {
  const handleClick = useCallback(() => {
    // doSomething
  })

  // ...
}
Enter fullscreen mode Exit fullscreen mode

⚡ React.memo

In addition, re-rendering of child components, which occurs when rendering parent components, can be avoided by using React.memo.

import { memo } from 'react'

// ...

const Child = memo((props) => {
  // ...
})
Enter fullscreen mode Exit fullscreen mode

Components wrapped in React.memo will return memoized values as long as the props do not change. This reduces unnecessary re-rendering.

Top comments (0)