What Happens When You Call store.getState() During Reducer Execution?
Redux throws a runtime error with the message "You may not call store.getState() while the reducer is executing" because the store locks state access during the dispatch phase to enforce pure reducer functions.
Understanding the constraints of Redux reducer execution is essential for writing predictable state management logic. In the reduxjs/redux codebase, the createStore implementation explicitly guards against calling store.getState() during reducer execution to prevent access to partially computed state. This architectural decision ensures that reducers remain pure functions of their arguments rather than relying on external state lookups.
The Error Mechanism in createStore.ts
The guard that prevents store.getState() during reducer execution is implemented in src/createStore.ts at lines 71-84. When a dispatch is in progress, Redux sets an internal isDispatching flag to true. The getState function checks this flag and throws immediately if it detects an active dispatch:
You may not call store.getState() while the reducer is executing.
The reducer has already received the state as an argument.
Pass it down from the top reducer instead of reading it from the store.
This error message explicitly guides developers toward the correct pattern: using the state argument passed to the reducer rather than querying the store.
Why Redux Prohibits store.getState() During Reducer Execution
Redux enforces this restriction for three architectural reasons that maintain the integrity of the state management pattern.
Enforcing Reducer Purity
Redux reducers must be pure functions of (state, action). Allowing store.getState() would introduce a hidden dependency on external state, breaking referential transparency. The reducer could observe different values depending on when it called the method, making the function impure and difficult to test.
Preventing Stale State Access
During a dispatch, the state is in transition. The reducer is computing the next state based on the current action. If getState() returned a value during this phase, it would expose either the previous state (which the reducer already has as an argument) or a partially computed next state from a different branch of a combined reducer. Both scenarios lead to subtle bugs and inconsistent behavior.
Avoiding Recursive Dispatch Issues
The isDispatching guard prevents accidental infinite loops. If a reducer could access the store, it might conditionally dispatch new actions based on the current state, leading to recursive dispatches that never terminate. By locking the store during reducer execution, Redux forces all side effects and subsequent logic into middleware or component layers where they can be properly managed.
Correct Patterns for Accessing State in Reducers
Instead of calling store.getState(), access state through the first argument provided to your reducer function.
The Correct Approach
function counterReducer(state = 0, action) {
// ✅ Correct: Use the `state` argument
switch (action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
default:
return state
}
}
The Prohibited Pattern
function faultyReducer(state = 0, action) {
// ❌ Incorrect: This throws an error during dispatch
if (action.type === 'CHECK') {
const currentState = store.getState()
return currentState > 10 ? 0 : state
}
return state
}
If you need to compare against previous state values or access other branches of a combined state tree, compose your reducers to receive the necessary state slices as arguments, or use selectors after the dispatch completes.
Verification in the Redux Test Suite
The behavior is verified in test/createStore.spec.ts at lines 513-522. The test suite explicitly asserts that calling getState() during reducer execution throws the expected error, ensuring this guard remains functional across Redux versions.
Summary
- Redux throws an error when you call
store.getState()during reducer execution to prevent impure function behavior. - The guard is implemented in
src/createStore.tsusing anisDispatchingflag that triggers during the dispatch phase. - Reducers must use the
stateargument provided to them rather than querying the store, ensuring pure functions and consistent state access. - This design prevents stale state reads, recursive dispatch loops, and side effects in reducers.
Frequently Asked Questions
Can I call store.getState() in middleware?
Yes. Middleware operates outside the reducer execution context, so store.getState() is fully accessible. Middleware receives store as an argument and can query state to make decisions about action processing, logging, or asynchronous flows.
What happens if I try to dispatch an action inside a reducer?
Similar to getState(), calling store.dispatch() during reducer execution throws an error: "You may not dispatch while the reducer is executing." This prevents recursive dispatches and ensures reducers remain pure calculations without side effects.
How do I access other parts of the state tree inside a reducer?
Reducers receive only their slice of state. To access other branches, use reducer composition with combineReducers or manual composition to pass necessary data as action payloads. Alternatively, perform cross-cutting logic in selectors or middleware after the dispatch completes.
Is there a way to read the current state for debugging during development?
Use Redux DevTools or middleware to inspect state. If you must log state within a reducer, use the state argument provided to the function. Never import the store instance into reducer files to call getState(), as this breaks module boundaries and purity guarantees.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →