React question detail
Is dispatch from useReducer asynchronous and does it update state immediately?
The dispatch function returned by useReducer is not asynchronous — it is a synchronous function call. When you call dispatch(action), React synchronously invokes your reducer with the current state and the action, computes the new state, and schedules a re-render. However, the state variable does not update immediately within the same render cycle. The updated state is only available in the next render.
This behavior is similar to useState's setState — React batches state updates for performance optimization, meaning the component does not re-render immediately after each dispatch call. Instead, React processes all dispatched actions and re-renders once with the final state.
Key Points
dispatchis synchronous: The reducer runs immediately whendispatchis called.- State update is not immediate in the current render: The state variable still holds the old value until the next render.
- React batches updates: Multiple
dispatchcalls within the same event handler result in a single re-render. - Reducer is a pure function: It computes the new state without side effects.
Example demonstrating that state does not update immediately
import React, { useReducer } from 'react';
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
default:
return state;
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, { count: 0 });
const handleClick = () => {
dispatch({ type: 'increment' });
console.log(state.count); // Still logs the OLD value (e.g., 0), not 1
dispatch({ type: 'increment' });
console.log(state.count); // Still logs the OLD value (e.g., 0), not 2
};
// After re-render, state.count will be 2 (both dispatches are processed)
return (
<button onClick={handleClick}>Increment Twice</button>
);
}
In the above example, even though dispatch is called twice, state.count still reflects the previous value inside the event handler. React batches both dispatches and re-renders the component once with count: 2.
How to read updated state after dispatch
If you need the updated value right after dispatching, you have several options:
- Use
useEffectto react to state changes:
useEffect(() => {
console.log('Updated count:', state.count);
}, [state.count]);
- Compute the next state manually:
const handleClick = () => {
const nextState = reducer(state, { type: 'increment' });
console.log('Next state will be:', nextState.count);
dispatch({ type: 'increment' });
};
- Use
useRefto track the latest state:
const stateRef = useRef(state);
useEffect(() => {
stateRef.current = state;
}, [state]);
Note: This behavior is by design in React. The dispatch function itself has a stable identity (it doesn't change between re-renders), which makes it safe to omit from useEffect dependency arrays.