Is this incorrect use of useCallback and useMemo?
Asked Answered
T

1

1

When should we worry about functional components redefining variables and functions??

I think this first case is clearly unnecessary, functions is not expensive to create(and the react dev tools profile test didn't detect performace change with useCallback)

import React, { useState, useMemo, useCallback } from "react";

export const App = () => {
  const [counter, setCounter] = useState(1);

  const showCounter = useCallback(() => {
    console.log(counter);
  }, [counter]);

  return (
    <>
      <button onClick={showCounter}>Show</button>
      <button onClick={() => setCounter(counter + 1)}>Add</button>
    </>
  );
};

In the other hand, in this other exemple, with react dev tools we can detect that useMemo cuts in half the time of rendering:

import React, { useState, useMemo, useCallback } from "react";

export const App = () => {
  const [counter, setCounter] = useState(1);

  const [list, setList] = useState<number[]>([]);

  function setListItem(item: number) {
    setList([...list, item]);
  }

  //should we useMemo here?
  const domList = useMemo(
    () =>
      list.map((value: number, index: number) => (
        <li key={index}> {value} </li>
      )),
    [list]
  );

  return (
    <>
      <p>{counter}</p>
      <button onClick={() => setCounter(counter - 1)}>Subtract</button>
      <button onClick={() => setCounter(counter + 1)}>Add</button>
      <button onClick={() => setListItem(counter)}>Push to array</button>
      <h1>List</h1>
      <ul>{domList}</ul>
    </>
  );
};

But still less than a millisecond of gain and array maps and JSX array are not something really expensive when we talk about front-end either. However, leaving an array map method run in each render is very uncomfortable to see. So, what of those things should we do? (Let's forget about when we have useEffect that depends of some variables that make the useMemo necessary)

  • useCallback and useMemo in all functions and variables that don't need to be redefined in each render.
  • useCallback and useMemo only in things that we can clearly see a performance gain(even if most time would not count as an improvement to UX). So we would need useMemo in this second example.
  • useCallback and useMemo only when not using it in a specific case we would get a great UX decline. So only really expensive functions would useCallback
Testee answered 6/3, 2021 at 20:35 Comment(2)
I think we should always have the code that's the most readable (no memo) until it becomes a problem and you need to optimize somethingLach
Check this reactjs.org/docs/optimizing-performance.html Lets first write the code, and then do the optimizations when and if we need it.Fruity
H
1

The performance you gain with each of them is different. Yes, for useMemo if the calculation you are memoizing is expensive, it offers a clear immediate benefit. However, for useCallback and useMemo, the long-term benefits refer to avoiding unnecessary renders in child components, not the recalculation cost.

Remember, if any of the props in a component change, they may trigger a render call, and the higher in the component tree this is the case, the more it will affect performance. That is the reason we normally use those hooks.

In general, components have several state elements, and with memo hooks, we can establish dependencies to avoid computational redundancy. For instance, in your synthetic case, you have two state elements: counter, and list. Each time counter changes, you prevented domList to be recalculated by making it dependent only on list. If this was a leaf node, sure no performance gain, however, this is the root of your component tree, and without those hooks, performance will not scale as you add more state elements and you will have to refactor your code to address performance. Sure, <ul>{domList}</ul> can be costly if the list has more than 1000 elements, but <ul><ExpensiveCutie domList={domList} /></ul> can take you weeks to notice, debug, and refactor.

In short, I recommend using memo hooks in container components and not in leaf components. Yet, what if your leaf component becomes a container as you are implementing it?

Before you go, do you even need to use memo()? Read more here.

Happen answered 6/3, 2021 at 20:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.