# How Redux Prevents Nested Dispatches Using the isDispatching Flag

> Discover how Redux prevents nested dispatches using the isDispatching flag to ensure atomic and deterministic state updates and avoid race conditions.

- Repository: [Redux/redux](https://github.com/reduxjs/redux)
- Tags: internals
- Published: 2026-03-05

---

**Redux uses an internal boolean flag called `isDispatching` to throw an error if code attempts to dispatch actions while a reducer is already executing, ensuring atomic and deterministic state updates.**

Redux guarantees that only one reducer runs at a time by guarding its dispatch mechanism with an internal lock. This protection centers on the **`isDispatching`** flag defined in [`src/createStore.ts`](https://github.com/reduxjs/redux/blob/main/src/createStore.ts), which prevents nested and concurrent dispatches that could lead to inconsistent state or unpredictable side effects.

## The isDispatching Flag Location and Purpose

### Flag Definition in createStore.ts

According to the Redux source code, the flag is declared as a module-level variable:

```typescript
let isDispatching = false;          // src/createStore.ts#L153

```

This boolean acts as a reentrancy guard that is initialized to `false` when the store is created and toggled exclusively within the `dispatch` function.

### Why Redux Blocks Reentrancy

Redux reducers must be **pure functions** that take the current state and an action, then return new state without side effects. Allowing a reducer to trigger another dispatch would create nested execution contexts where the state is mid-update, violating the assumption that the state snapshot passed to the reducer is stable. The `isDispatching` flag enforces this single-execution guarantee.

## How Redux Blocks Nested Dispatches in the dispatch Function

The `dispatch` implementation in [`src/createStore.ts`](https://github.com/reduxjs/redux/blob/main/src/createStore.ts) wraps reducer execution in a strict check-and-lock pattern:

```typescript
function dispatch(action: A) {
  // … validation logic …

  if (isDispatching) {
    throw new Error('Reducers may not dispatch actions.'); // src/createStore.ts#L3003‑L3005
  }

  try {
    isDispatching = true;                                 // src/createStore.ts#L3008
    currentState = currentReducer(currentState, action);
  } finally {
    isDispatching = false;                                // src/createStore.ts#L3011‑L3012
  }

  // … notify listeners …
}

```

**Key mechanisms:**

- **Pre-dispatch guard:** Before the reducer runs, Redux checks `isDispatching`. If `true`, it throws immediately, preventing nested dispatches.
- **Atomic execution:** The flag is set to `true` only after the check passes and cleared in a `finally` block, ensuring the lock releases even if the reducer throws an exception.
- **Single-thread safety:** Because JavaScript is single-threaded, this boolean flag is sufficient to prevent concurrent dispatches without complex mutexes.

## Protecting Other Store Methods During Dispatch

Redux extends the `isDispatching` guard beyond the `dispatch` method to maintain consistency across the store API.

### Preventing getState Calls During Reducer Execution

The `getState` method refuses to return state while a reducer is active:

```typescript
function getState(): S {
  if (isDispatching) {
    throw new Error(
      'You may not call store.getState() while the reducer is executing…' // src/createStore.ts#L177‑L182
    );
  }
  return currentState as S;
}

```

This prevents reducers from reading potentially inconsistent intermediate state.

### Blocking Subscription Changes Mid-Dispatch

Both adding and removing listeners are prohibited during dispatch:

- **Subscribe guard:** Lines 220‑227 in [`src/createStore.ts`](https://github.com/reduxjs/redux/blob/main/src/createStore.ts) check `isDispatching` before registering a new listener.
- **Unsubscribe guard:** Lines 240‑247 apply the same check when removing a listener.

These guards ensure the listener array remains stable while Redux iterates over it to notify subscribers after a state change.

## Practical Code Examples

### Illegal Nested Dispatch That Throws

Attempting to dispatch from within a reducer triggers the guard immediately:

```javascript
import { createStore } from 'redux';

function counter(state = 0, action) {
  switch (action.type) {
    case 'inc':
      // ❌ illegal: tries to dispatch while reducer is running
      store.dispatch({ type: 'inc' });
      return state + 1;
    default:
      return state;
  }
}

const store = createStore(counter);
store.dispatch({ type: 'inc' });
// => Error: Reducers may not dispatch actions.

```

The error originates from the `isDispatching` check in `dispatch` (lines 3003‑3005).

### Safe Dispatch from a Listener

Dispatching from a subscriber is valid because it occurs after the current dispatch completes:

```javascript
import { createStore } from 'redux';

let ready = false;

function reducer(state = { value: 0 }, action) {
  switch (action.type) {
    case 'init':
      ready = true;
      return state;
    case 'inc':
      return { value: state.value + 1 };
    default:
      return state;
  }
}

const store = createStore(reducer);
store.subscribe(() => {
  if (ready) store.dispatch({ type: 'inc' }); // allowed: runs after dispatch finishes
});

store.dispatch({ type: 'init' });
console.log(store.getState()); // { value: 0 }
store.dispatch({ type: 'inc' });
console.log(store.getState()); // { value: 1 }

```

The listener runs **after** the current dispatch finishes, so `isDispatching` is `false` and the subsequent dispatch succeeds.

### Preventing getState Inside a Reducer

Reading state mid-reduction is blocked to avoid inconsistent snapshots:

```javascript
function badReducer(state = { count: 0 }, action) {
  if (action.type === 'inc') {
    // ❌ illegal: calling getState while reducer runs
    console.log(store.getState());
    return { count: state.count + 1 };
  }
  return state;
}

```

Calling `store.getState()` inside the reducer throws the error defined at lines 177‑182 of [`src/createStore.ts`](https://github.com/reduxjs/redux/blob/main/src/createStore.ts).

## Summary

- Redux guarantees **single, atomic reducer execution** using an internal `isDispatching` flag defined in [`src/createStore.ts`](https://github.com/reduxjs/redux/blob/main/src/createStore.ts).
- The **`dispatch`** function checks `isDispatching` before invoking the reducer and throws "Reducers may not dispatch actions." if a nested dispatch is detected.
- A **`try…finally`** block ensures `isDispatching` resets to `false` even if the reducer throws, preventing lock starvation.
- **`getState`**, **`subscribe`**, and the **unsubscribe** function all share the same guard, throwing errors if called while a reducer is executing.
- Because JavaScript is single‑threaded, this boolean flag is sufficient to prevent concurrent dispatches without complex synchronization primitives.

## Frequently Asked Questions

### What happens if a reducer tries to dispatch an action?

Redux throws an error with the message "Reducers may not dispatch actions." This occurs because the `dispatch` function in [`src/createStore.ts`](https://github.com/reduxjs/redux/blob/main/src/createStore.ts) checks the `isDispatching` flag at lines 3003‑3005 before executing the reducer. If the flag is already `true`, the dispatch is rejected immediately to prevent nested execution.

### Can I call getState inside a reducer?

No. Calling `store.getState()` while a reducer is executing throws an error: "You may not call store.getState() while the reducer is executing…" The `getState` implementation in [`src/createStore.ts`](https://github.com/reduxjs/redux/blob/main/src/createStore.ts) (lines 177‑182) checks `isDispatching` and blocks the call to prevent reading inconsistent intermediate state.

### Why does Redux allow dispatching from listeners but not reducers?

Dispatching from listeners is safe because listeners execute **after** the current dispatch cycle completes. By the time the subscriber array is notified, the `isDispatching` flag has been reset to `false` in the `finally` block (lines 3011‑3012). In contrast, reducers run **while** `isDispatching` is `true`, so attempting to dispatch from within a reducer would create nested execution, violating Redux's atomic update guarantee.

### Is the isDispatching flag part of the public Redux API?

No. The `isDispatching` flag is an internal implementation detail of [`createStore.ts`](https://github.com/reduxjs/redux/blob/main/createStore.ts) and is not exposed to consumers of the store. It exists solely as a runtime safeguard to enforce Redux's architectural constraints. You cannot access or modify this flag from application code; it is managed entirely within the Redux library's internal closure.