What are production use cases for the useRef, useMemo, useCallback hooks?
Asked Answered
S

3

33

Outside of the counter example seen in many YouTube tutorial videos, what are practical/real-world use cases for useMemo and useCallback?

Also, I've only seen an input focus example for the useRef hook.

Please share other use cases you've found for these hooks.

Spencer answered 1/3, 2021 at 20:29 Comment(5)
useRef can be used to store local mutable value in a component. It doesn't participate in rerendering (unline state data). useMemo is used to memoize (like we do in Dynamic Programming, concept wise) and skip recalculation. It is useful when you don't want to recalculate heavy calculations each time a component renders. useCallback is used to avoid recreating / redefining methods at every render.Tar
Just wanted to understand your rationale. I think those use cases you refer to are every bit as real-world as any. And if you're unable to extrapolate their use into an actual app... well, I doubt anybody will give you a clinic on how to properly use them. Ajeet's superficial comment is about as good as it gets.Atonement
@AjeetShah Thank you. Could you share examples of what those heavy calculations would be? I've only been introduced to the counter example.Spencer
For example, you have some data in state, say, array of 100 objects, and you want to do some filtering and sorting before displaying that data at UI. If you don't memoize this sorting / filtering calculation, it will be done each time the component re-renders (even when some other state var changes).Tar
Related: https://mcmap.net/q/453997/-is-this-incorrect-use-of-usecallback-and-usememo/2873538Tar
T
94

useRef:

Syntax: const refObject = useRef(initialValue);

It simply returns a plain JavaScript object. Its value can be accessed and modified (mutability) as many times as you need without worrying about "rerender".

Its value will persist (won't be reset to the initialValue unlike an ordinary* object defined in your function component; it persists because useRef gives you the same object instead of creating a new one on subsequent renders) for the component lifetime.

If you write const refObject = useRef(0) and print refObject on console, you would see the log an object - { current: 0 }.

*ordinary object vs refObject, example:

function App() {
  const ordinaryObject = { current: 0 } // It will reset to {current:0} at each render
  const refObject = useRef(0) // It will persist (won't reset to the initial value) for the component lifetime
  return <>...</>
}

Few common uses, examples:

  1. To access the DOM: <div ref={myRef} />
  2. Store mutable value like instance variable (in class)
  3. A render counter
  4. A value to be used in setTimeout / setInterval without a stale closure issue.

useMemo:

Syntax: const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

It returns a memoized value. The primary purpose of this hook is "performance optimization". Use it sparingly to optimize the performance when needed.

It accepts two arguments - "create" function (which should return a value to be memoized) and "dependency" array. It will recompute the memoized value only when one of the dependencies has changed.

Few common uses, examples:

  1. Optimize expensive calculations (e.g. operations on data like sort, filter, changing format etc.) while rendering

Unmemoized example:

function App() {
  const [data, setData] = useState([.....])

  function format() {
    console.log('formatting ...') // this will print at every render
    const formattedData = []
    data.forEach(item => {
      const newItem = // ... do somthing here, formatting, sorting, filtering (by date, by text,..) etc
      if (newItem) {
        formattedData.push(newItem)
      }
    })
    return formattedData
  }

  const formattedData = format()

  return <>
    {formattedData.map(item => <div key={item.id}>
      {item.title}
    </div>)}
  </>
}

Memoized example:

function App() {
  const [data, setData] = useState([.....])

  function format() {
    console.log('formatting ...') // this will print only when data has changed
    const formattedData = []
    data.forEach(item => {
      const newItem = // ... do somthing here, formatting, sorting, filtering (by date, by text,..) etc
      if (newItem) {
        formattedData.push(newItem)
      }
    })
    return formattedData
  }

  const formattedData = useMemo(format, [data])

  return <>
    {formattedData.map(item => <div key={item.id}>
      {item.title}
    </div>)}
  <>
}

useCallback:

Syntax: const memoizedCallback = useCallback(() => { //.. do something with a & b }, [a, b])

It returns a memoized function (or callback).

It accepts two arguments - "function" and "dependency" array. It will return new i.e. re-created function only when one of the dependencies has changed, or else it will return the old i.e. memoized one.

Few common uses, examples:

  1. Passing memoized functions to child components (that are optimized with React.memo or shouldComponentUpdate using shallow equal - Object.is) to avoid unnecessary rerender of child component due to functions passed as props.

Example 1, without useCallback:

const Child = React.memo(function Child({foo}) {
  console.log('child rendering ...') // Child will rerender (because foo will be new) whenever MyApp rerenders
  return <>Child<>
})

function MyApp() {
  function foo() {
    // do something
  }
  return <Child foo={foo}/>
}

Example 1, with useCallback:

const Child = React.memo(function Child({foo}) {
  console.log('child rendering ...') // Child will NOT rerender whenever MyApp rerenders
  // But will rerender only when memoizedFoo is new (and that will happen only when useCallback's dependency would change)
  return <>Child<>
})

function MyApp() {
  function foo() {
    // do something
  }
  const memoizedFoo = useCallback(foo, [])
  return <Child foo={memoizedFoo}/>
}
  1. Passing memoized functions to as dependencies in other hooks.

Example 2, without useCallback, Bad (But eslint-plugin-react-hook would give you warning to correct it):

function MyApp() {
  function foo() {
    // do something with state or props data
  }
  useEffect(() => {
    // do something with foo
    // maybe fetch from API and then pass data to foo
    foo()
  }, [foo])
  return <>...<>
}

Example 2, with useCallback, Good:

function MyApp() {
  const memoizedFoo = useCallback(function foo() {
    // do something with state or props data
  }, [ /* related state / props */])

  useEffect(() => {
    // do something with memoizedFoo
    // maybe fetch from API and then pass data to memoizedFoo
    memoizedFoo()
  }, [memoizedFoo])
  return <>...<>
}

These hooks rules or implementations may change in the future. So, please make sure to check hooks reference in docs. Also, it is important to pay attention to eslint-plugin-react-hook warnings about dependencies. It will guide you if omit any dependency of these hooks.

Tar answered 14/3, 2021 at 15:51 Comment(1)
I appreciate this, Ajeet! Thanks for including the use cases!Spencer
W
4

I want to add, for useMemo i usually use it when i want to combine useState and useEffect at the same time. For example:

...
const [data, setData] = useState(...);
const [name, setName] = useState("Mario");
// like the example by ajeet, for complex calculations
const formattedData = useMemo(() => data.map(...), [data])
// or for simple state that you're sure you would never modify it directly
const prefixedName = useMemo(() => NAME_PREFIX + name, [name]);

I do not know if there will be performance issues because the docs stated that useMemo should be used for expensive calculation. But i believe this is way cleaner than using useState

Whittemore answered 16/6, 2021 at 3:12 Comment(2)
useMemo is a lot slower than useState for inexpensive calculation based on this articleQuantify
@DennisYang The link is brokenSeessel
E
-5

useMemo always use for performance optimization. Be careful to add all the deps need.

Equalitarian answered 22/1, 2022 at 13:26 Comment(2)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Colubrine
UseMemo does not comes for free. It should only be used for expensive calculations, according to this article and react docs react.dev/reference/react/useMemo developerway.com/posts/how-to-use-memo-use-callbackLammond

© 2022 - 2024 — McMap. All rights reserved.