Should I include setState in useCallback's array of dependencies?
Asked Answered
P

3

45
    const [active, setActive] = useState(false);

    const onActiveChanged = useCallback(
      isActive => () => {
        // do something
        setActive(isActive);
      },
      [setActive], // or just [] is okay?
    );

When using useState and useCallback (or useMemo) together, should I include setState in the array of dependencies?

Pallette answered 18/4, 2019 at 1:59 Comment(3)
@AngelSalazar The updater method from useState isn't memoized or doesn't persist the same reference?Pallette
my bad, according to the docs "React guarantees that setState function identity is stable and won’t change on re-renders. This is why it’s safe to omit from the useEffect or useCallback dependency list."Sayer
@Sayer Thanks for confirmation! I also found that part in github.com/reactjs/reactjs.org/blob/master/content/docs/… So just passing an empty array must be okay. You can leave it as an answer to this question or I'll post an answer to my own question.Pallette
R
66

The recommendation for that is also on React Docs - Hooks API Reference.

The setState function is used to update the state. It accepts a new state value and enqueues a re-render of the component.

setState(newState);

During subsequent re-renders, the first value returned by useState will always be the most recent state after applying updates.

Note

React guarantees that setState function identity is stable and won’t change on re-renders. This is why it’s safe to omit from the useEffect or useCallback dependency list.

Rating answered 18/4, 2019 at 10:6 Comment(2)
This answer is slightly obsolete now since it refers to obsolete docs. The new docs actually do not promise that the setter will be stable, but maybe that's just an oversight?Sining
That's still valid. It's mentioned on the useCallback page: react.dev/reference/react/…Seaward
O
3

The purpose of useCallback as you rightly hinted is to memoise:

useCallback(fn, deps) is equivalent to useMemo(() => fn, deps).

And as for what useMemo is intended for:

You may rely on useMemo as a performance optimization, not as a semantic guarantee.

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

But as a rule, useState is stable between renders (i.e. pre-memoised), so you should not need to memoise it again, so it can be safely omited from dependency callbacks.

The question then comes, is your 'do something' below an expensive calculation? It shouldn't be to onerous to make use of useCallback, but it could well be boilerplate code you don't need, and could make almost direct use of your setActive function.

const [active, setActive] = useState(false);

const onActiveChanged = useCallback(
  isActive => () => {
    // do something
    setActive(isActive);
  },
  [],
);

Another way to prevent unnecessary dependencies, in your useCallback and other hooks, is to make use of functional updates. The result being that you can have these:

const [active, setActive] = useState(false);
const [expensiveCalc, setExpensiveCalc] = useState(false);

const onExpensiveCalc = useCallback(
  expensiveInput => () => {
    const newState = doExpensiveCalc(expensiveInput);
    expensiveCalc(newState);
  },
  [setActive], // here for completeness only
);

return (<>
  // expensive calculation
  <button onClick={onExpensiveCalc}>Do lengthy calculation</button>
  // cheap calculation, using functional updates
  <button onClick={() => setActive(prevBoolean => !prevBoolean)}>Cheap Set Active</button>
</>)


Do note, that there's a little nuance to how set state works in an onClick, and you should make use of an arrow function, so your setActive is run on click, not render. This is shown in the second answer above, but without explanation.

See also: What is useState() in React?

Outwardbound answered 24/10, 2021 at 11:41 Comment(0)
R
-2

you don't need to pass the setActive as it will never change, as it is react setState function not a state. so you can pass an empty array instead, since passing setActive won't change the behaviour of the useCallback anyways.

Rebarebah answered 24/6 at 6:54 Comment(3)
1. in general function references can change like any object reference. 2. setState is stable, but other answers already stated that.Slicer
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.Gravitate
@Slicer yes, they can change, but not in the case of setActive, as it will remain same throughout the code rerendersRebarebah

© 2022 - 2024 — McMap. All rights reserved.