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>;
}