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?