How to fix useReducer: the reducer function did not return a valid new state in React
This React error occurs when a useReducer reducer function fails to return a proper state object, often due to missing return statements, unhandled action types, or returning undefined. The reducer must be a pure function that always returns a valid next state for every possible action. Understanding common patterns and debugging techniques helps prevent this state management issue.
Quick fix
Fixes useReducer: the reducer function did not return a valid new state
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { ...state, count: state.count + 1 };
case 'decrement':
return { ...state, count: state.count - 1 };
default:
// Option 1: Return current state (silently ignore unknown actions)
// return state;
// Option 2: Throw error during development
throw new Error(`Unknown action type: ${action.type}`);
}
}- Check the browser console for the exact error location
- Add a default case that throws an error for unknown actions
- Verify every case branch returns a valid state
- Use TypeScript to catch missing returns at compile time
function reducer(state, action) {switch (action.type) {case 'increment':return { ...state, count: state.count + 1 };case 'decrement':return { ...state, count: state.count - 1 };default:// Option 1: Return current state (silently ignore unknown actions)// return state;// Option 2: Throw error during developmentthrow new Error(`Unknown action type: ${action.type}`);}}useReducer: the reducer function did not return a valid new state
What this error means
React's useReducer hook expects the reducer function to be a pure function that takes the current state and an action, and returns the next state. This error occurs when the reducer fails to return a valid state object, typically by returning undefined, forgetting a return statement, or not handling all possible action types. When React calls the reducer during state updates, it expects a concrete state value to use for the next render. If the reducer returns undefined or nothing at all, React cannot proceed with the state update and throws this error. This is a critical issue because it breaks the fundamental contract of reducers in React's state management system.
Typical symptoms
- Console error: "useReducer: the reducer function did not return a valid new state"
- Component state becomes undefined after dispatching certain actions
- UI stops updating or shows stale data after user interactions
- React dev tools showing undefined state for components using useReducer
- Actions that previously worked suddenly cause the component to break
- Inconsistent behavior where some actions work but others don't
- TypeScript errors about state being possibly undefined when accessing properties
Common causes
- Missing return statement in one or more case branches of a switch statement
- Reducer function not handling all possible action types (no default case)
- Explicitly returning undefined from a case branch
- Forgetting to return state in the default case when using switch statements
- Mutating state directly without returning a new object
- Using if/else statements without covering all possible conditions
- Reducer falling through to the end of the function without a return
- Action type that doesn't match any case in a switch statement
How to fix "useReducer: the reducer function did not return a valid new state"
Open your browser's developer tools (F12) and look for the error message. React usually includes a stack trace showing which component and reducer function is causing the issue. Note the line number and component name to locate the problematic reducer.
Ensure your reducer has a default case that either returns the current state or throws a descriptive error. This catches any unhandled action types during development.
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { ...state, count: state.count + 1 };
case 'decrement':
return { ...state, count: state.count - 1 };
default:
// Option 1: Return current state (silently ignore unknown actions)
// return state;
// Option 2: Throw error during development
throw new Error(`Unknown action type: ${action.type}`);
}
}Check each case in your switch statement (or each condition in if/else) to ensure it ends with a return statement. Common mistakes include using break instead of return, or forgetting to return after mutating state.
// WRONG - missing return after mutation
function badReducer(state, action) {
switch (action.type) {
case 'update':
state.value = action.payload; // Mutation!
break; // Should be return { ...state, value: action.payload };
case 'reset':
return initialState;
}
// No return here - ERROR!
}
// CORRECT - every case returns
function goodReducer(state, action) {
switch (action.type) {
case 'update':
return { ...state, value: action.payload };
case 'reset':
return initialState;
default:
return state;
}
}TypeScript can help identify branches that don't return a value. Configure your tsconfig.json with strict mode and enable noImplicitReturns to get warnings about functions that might not return a value.
{
"compilerOptions": {
"strict": true,
"noImplicitReturns": true
}
}TypeScript will then warn you:
function reducer(state: State, action: Action): State {
switch (action.type) {
case 'increment':
return { ...state, count: state.count + 1 };
// TypeScript error: Not all code paths return a value
}
}If you're using if/else statements, consider converting to a switch statement which makes it clearer that each branch must return. Switch statements are generally preferred for reducers because they explicitly show all possible action types.
// Hard to track - if/else
function ifElseReducer(state, action) {
if (action.type === 'add') {
return { ...state, items: [...state.items, action.item] };
} else if (action.type === 'remove') {
return { ...state, items: state.items.filter(i => i.id !== action.id) };
} // Missing else clause!
}
// Clearer - switch statement
function switchReducer(state, action) {
switch (action.type) {
case 'add':
return { ...state, items: [...state.items, action.item] };
case 'remove':
return { ...state, items: state.items.filter(i => i.id !== action.id) };
default:
return state;
}
}Create unit tests for your reducer that dispatch each possible action type and verify the returned state. This helps catch missing returns and ensures all action types are handled.
import { describe, it, expect } from 'vitest';
import reducer from './reducer';
describe('reducer', () => {
const initialState = { count: 0 };
it('handles increment action', () => {
const result = reducer(initialState, { type: 'increment' });
expect(result).toEqual({ count: 1 });
});
it('handles decrement action', () => {
const result = reducer(initialState, { type: 'decrement' });
expect(result).toEqual({ count: -1 });
});
it('returns current state for unknown actions', () => {
const result = reducer(initialState, { type: 'unknown' });
expect(result).toBe(initialState);
});
});For complex reducers with many cases, break out logic into helper functions. This makes each case simpler and reduces the chance of forgetting a return statement.
function updateUser(state, action) {
return {
...state,
users: state.users.map(user =>
user.id === action.id ? { ...user, ...action.updates } : user
)
};
}
function addUser(state, action) {
return {
...state,
users: [...state.users, action.user]
};
}
function reducer(state, action) {
switch (action.type) {
case 'UPDATE_USER':
return updateUser(state, action);
case 'ADD_USER':
return addUser(state, action);
case 'DELETE_USER':
return deleteUser(state, action);
default:
return state;
}
}Advanced notes
The useReducer hook is particularly sensitive to this error because reducers are meant to be pure functions with predictable behavior. In production builds, React might handle missing returns differently than in development. Some state management libraries like Redux Toolkit provide utilities (createSlice) that automatically generate reducers with proper return handling. For complex applications, consider using immer with useReducer to write mutable-looking code that automatically produces immutable updates. When debugging, remember that React Strict Mode double-invokes reducers in development, which can help catch impure reducer behavior early. If you're migrating from class component setState to useReducer, note that setState merges updates automatically while reducers must explicitly return complete state objects.
Frequently asked questions
- What does "useReducer: the reducer function did not return a valid new state" mean?
- React's useReducer hook expects the reducer function to be a pure function that takes the current state and an action, and returns the next state. This error occurs when the reducer fails to return a valid state object, typically by returning undefined, forgetting a return statement, or not handling all possible action types. When React calls the reducer during state updates, it expects a concrete state value to use for the next render.
- How do I fix "useReducer: the reducer function did not return a valid new state"?
- This React error occurs when a useReducer reducer function fails to return a proper state object, often due to missing return statements, unhandled action types, or returning undefined. The reducer must be a pure function that always returns a valid next state for every possible action. Understanding common patterns and debugging techniques helps prevent this state management issue.
- What causes "useReducer: the reducer function did not return a valid new state"?
- Missing return statement in one or more case branches of a switch statement Reducer function not handling all possible action types (no default case) Explicitly returning undefined from a case branch
- Is the "useReducer: the reducer function did not return a valid new state" error serious?
- Yes. This is a high-severity error that usually blocks work until it is fixed, but the steps above resolve it.
Official resources & further reading
- ๐React Official Documentation
- ๐React useReducer API documentation(Official Docs)
- ๐React reducer pattern guide(Official Docs)
- ๐Redux immutable update patterns(Official Docs)
- ๐