Handling Non-Serializable Values in Redux Store
Using non-serializable values in your Redux store is generally discouraged, but there are cases where you might need to store non-serializable data. Here’s how to handle this situation properly:
Why Redux Prefers Serializable Values
- Debugging: The Redux DevTools rely on serialization
- Time-travel debugging: Requires store state to be serializable
- Persistence: Storing state to localStorage or sending to server
- Middleware: Many Redux middlewares expect serializable actions/state
Common Non-Serializable Values
- Class instances
- Functions
- Promises
- Symbols
- DOM elements
- React components
- Complex objects with circular references
Solutions
1. Recommended: Transform Data Before Storing
// Before storing
const serializableData = {
date: nonSerializableDate.toISOString(),
function: 'someFunctionName' // store identifier instead
};
// When retrieving
const originalData = {
date: new Date(serializableData.date),
function: functionMap[serializableData.function]
};
2. Use Redux Toolkit’s createSlice
with Custom Middleware
Redux Toolkit automatically checks for serializability:
import { configureStore } from '@reduxjs/toolkit';
const store = configureStore({
reducer: rootReducer,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: {
// Ignore these action types
ignoredActions: ['some/nonSerializableAction'],
// Ignore these paths in the state
ignoredPaths: ['items.dates']
}
})
});
3. Store References Instead of Instances
// Instead of storing the entire class instance
class User {
constructor(id, name) {
this.id = id;
this.name = name;
}
}
// Store plain objects
const userState = {
currentUser: { id: 1, name: 'John' }
};
4. Use Custom Serialization
For special cases like Dates:
// Action creator
const setDate = (date) => ({
type: 'SET_DATE',
payload: date.toISOString() // Serialize
});
// Reducer
const reducer = (state = {}, action) => {
switch (action.type) {
case 'SET_DATE':
return {
...state,
date: new Date(action.payload) // Deserialize
};
default:
return state;
}
};
When to Break the Rules
You might intentionally store non-serializable values when:
- Performance is critical and serialization is expensive
- Using libraries that require storing non-serializable objects
- The data is truly transient and never needs persistence
Best Practices
- Always prefer serializable data when possible
- Document any intentional non-serializable values
- Isolate non-serializable values to specific parts of your state
- Consider alternative state management for complex non-serializable state (React context, component state)