What Validation Does Redux Perform on Action Objects in Dispatch?
Redux validates every dispatched action by verifying it is a plain JavaScript object with a defined string type property, throwing descriptive errors immediately if any check fails.
When you call store.dispatch(action) in the reduxjs/redux repository, the library enforces strict requirements before the action reaches your reducers. These validation steps ensure action serializability, enable time-travel debugging, and maintain predictable state management across your application.
The Three-Step Validation Process in Redux Dispatch
The dispatch function in src/createStore.ts performs three sequential validation checks on every action object between lines 81 and 101.
Step 1: Plain Object Validation
First, Redux verifies the action is a plain JavaScript object using the isPlainObject utility from src/utils/isPlainObject.ts. This check fails for class instances, arrays, functions, or null values.
In src/createStore.ts (lines 81-84), if this check fails, Redux throws:
if (!isPlainObject(action)) {
throw new Error(
`Actions must be plain objects. Instead, the actual type was: '${kindOf(
action
)}'. You may need to add middleware …`
)
}
Step 2: Type Property Existence Check
Second, Redux checks that the type property is not undefined. This validation occurs at lines 89-93 of src/createStore.ts:
if (typeof action.type === 'undefined') {
throw new Error(
'Actions may not have an undefined "type" property. You may have misspelled an action type string constant.'
)
}
Step 3: Type Property String Validation
Finally, Redux verifies that action.type is a string. This check at lines 95-101 prevents numeric or symbolic action types that could break serialization:
if (typeof action.type !== 'string') {
throw new Error(
`Action "type" property must be a string. Instead, the actual type was: '${kindOf(
action.type
)}'. Value was: '${action.type}' (stringified)`
)
}
Source Code Locations and Implementation Details
The validation logic spans several utility files in the reduxjs/redux repository:
| File | Purpose |
|---|---|
src/createStore.ts |
Contains the dispatch function with the three validation checks at lines 81-101. |
src/utils/isPlainObject.ts |
Exports isPlainObject to verify the action is a plain object, not a class instance or array. |
src/utils/isAction.ts |
Combines all three checks into a reusable predicate for external validation. |
src/utils/kindOf.ts |
Generates human-readable type names used in error messages. |
Practical Examples of Action Validation
Valid Action Dispatch
import { createStore } from 'redux'
function reducer(state = 0, action) {
switch (action.type) {
case 'INCREMENT': return state + 1
default: return state
}
}
const store = createStore(reducer)
store.dispatch({ type: 'INCREMENT', payload: 1 })
// ✅ No error – plain object with string type
Invalid: Class Instance Action
class ActionClass {
constructor() {
this.type = 'ADD_TODO'
}
}
store.dispatch(new ActionClass())
// ❌ Throws: "Actions must be plain objects. Instead, the actual type was: 'object'."
Invalid: Missing Type Property
store.dispatch({ payload: 42 })
// ❌ Throws: "Actions may not have an undefined "type" property."
Invalid: Non-String Type
store.dispatch({ type: 123 })
// ❌ Throws: "Action "type" property must be a string. Instead, the actual type was: 'number'."
Handling Non-Plain Actions with Middleware
Redux's validation only applies to the core dispatch function. Middleware like redux-thunk intercepts actions before they reach the validation logic, allowing you to dispatch functions or other non-plain objects:
import thunk from 'redux-thunk'
import { applyMiddleware, createStore } from 'redux'
const store = createStore(reducer, applyMiddleware(thunk))
// Dispatch a function (thunk) instead of a plain object
store.dispatch((dispatch, getState) => {
setTimeout(() => {
dispatch({ type: 'ASYNC_DONE' })
}, 1000)
})
// ✅ No error – thunk middleware intercepts the function before validation
Summary
Redux enforces three strict validation rules on every action dispatched to the store:
- Plain Object Requirement: Actions must be plain JavaScript objects, not class instances or arrays, verified by
isPlainObjectinsrc/utils/isPlainObject.ts. - Type Property Existence: The
typeproperty must be defined and cannot beundefined, checked at lines 89-93 ofsrc/createStore.ts. - String Type Requirement: The
typeproperty must be a string value, validated at lines 95-101 ofsrc/createStore.ts.
These checks ensure action serializability and predictable reducer behavior, while middleware provides escape hatches for advanced patterns like thunks.
Frequently Asked Questions
What happens if I dispatch a class instance instead of a plain object?
Redux throws an error stating "Actions must be plain objects" because class instances fail the isPlainObject check in src/utils/isPlainObject.ts. To fix this, use object literals like { type: 'ACTION' } or ensure your action creators return plain objects rather than class instances.
Can I use symbols for action types in Redux?
No, Redux explicitly requires the type property to be a string. The validation at lines 95-101 of src/createStore.ts checks typeof action.type !== 'string' and throws an error if you pass a symbol, number, or other non-string value. This requirement ensures action types are serializable and can be logged or replayed.
Why does Redux require actions to be plain objects?
Plain objects are serializable, which enables Redux DevTools features like time-travel debugging, state persistence, and action replay. The isPlainObject utility ensures actions can be safely logged, stored, and rehydrated without losing information or carrying unexpected prototype chains.
How can I dispatch functions or promises if Redux only accepts plain objects?
You can use middleware like redux-thunk or redux-promise to intercept non-plain actions before they reach the core dispatch validation. Middleware sits between your store.dispatch() call and the validation logic in src/createStore.ts, allowing you to dispatch thunks, promises, or other custom payloads while still eventually dispatching plain objects to the reducers.
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 →