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?
useCallback also takes its own dependency array, which you can use to pass any variables that the function depends on. For example, let's say that the component actually takes in a url prop, which is used in the API request.
The useCallback hook is used when you have a component in which the child is rerendering again and again without need. Pass an inline callback and an array of dependencies. useCallback will return a memoized version of the callback that only changes if one of the dependencies has changed.
Dependency arrays are a concept that is tightly coupled to hooks in React (thus also to function components). Some hooks, like useEffect and useCallback have 2 arguments. The first one is a callback (a function), and the second one is the dependency array. It takes the form of an array of variables.
With useCallback you can define a function that has referential equality between renders. You can use useMemo to calculate a value that has referential equality between renders.
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.
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.
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); }, [setActive], // or just [] is okay? );
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 ); 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?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With