Why we should passing a function to setState instead of value?
When working with setState we have 2 options. Let say we have
const [count, setCount] = useState(0)
// Option 1: Using value
setState(count + 1)
// Option 2: Using a function
setState(previousCount => previousCount + 1)
It looks kind of similar but we should use option 2 when new state depends on previous state
1. State Updates Are Asynchronous:
React batches state updates for performance optimization. This means that if multiple setState
calls are made in quick succession, the updates may not be applied immediately.
const [count, setCount] = useState(0);
const incrementTwice = () => {
setCount(count + 1); // This uses the current value of `count` = 0
setCount(count + 1); // This also uses the current value of `count` = 0, not the updated one (1)
};
incrementTwice(); // count will be 1 instead of 2 when incrementTwice is called first time.
Using a Function Solves This:
const incrementTwice = () => {
setCount(prevCount => prevCount + 1); // Uses the updated state
setCount(prevCount => prevCount + 1); // Correctly increments again
};
incrementTwice(); // Result: 2
2. Avoid Race Conditions:
When the new state depends on the current state, using a function ensures that each update is based on the latest state, preventing race conditions.
This is especially important in asynchronous operations like event handlers or setTimeout
.
3. When working along with useEffect
When we only use setState inside an useEffect. Using function can allow us to exclude the state variable from dependencies array
useEffect(() => {
if (condition) setCount(count + 1);
}, [count]); // Need to include `count` in the dependency array
useEffect(() => {
if (condition) setCount(prevCount => prevCount + 1);
}, []); // No need to include `count` in the dependency array
Using both state, setState inside useEffect can sometimes cause infinite loop
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
// Every time the component renders, this will update the count
setCount(count + 1); // This will cause a re-render and trigger the effect again
}, [count]); // Dependency on `count`, but we're updating `count` inside the effect, so it causes a loop
return <h1>Count: {count}</h1>;
}