🔥 Hot React Tips: useState Accepts a Function Argument
If you’re using React’s useState
hooks in your frontend application, you may not know that the setter function can also accept a function as an argument. This can be used to avoid unnecessary re-renders when combined with useCallback
.
Consider the following example:
const MyCounter = () => {
const [counterValue, setCounterValue] = useState(0);
const incrementCounter = useCallback(() => {
setCounterValue(counterValue + 1);
}, [counterValue]);
return (
<>
<Text>{counterValue}</Text>
<Button onClick={incrementCounter} title="Increment Counter" />
</>
);
};
Everything looks great. We have a memoised callback which will increment the counterValue
variable. However, it’s actually not that great: if you look closely, this memoised callback is going to get a new identity each time that counterValue
changes.
A more efficient way to represent could be as follows:
const MyCounter = () => {
const [counterValue, setCounterValue] = useState(0);
const incrementCounter = useCallback(() => {
setCounterValue((previousCounterValue) => previousCounterValue + 1);
}, []);
return (
<>
<Text>{counterValue}</Text>
<Button onClick={incrementCounter} title="Increment Counter" />
</>
);
};
Because the setter returned by useState
also accepts a function argument which will be invoked with the previous state value, we can simply use the previous value to compute the new counter value. This also avoids changing any dependencies of useCallback
and so the identity of the callback never changes.
Summary
- Use the functional argument of the
useState
setter to access the previous state value - Avoid identity changes caused by inefficient usage of
useCallback
which can cause unnecessary re-renders