Using non-serializable values in Redux store

Loading

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

  1. Debugging: The Redux DevTools rely on serialization
  2. Time-travel debugging: Requires store state to be serializable
  3. Persistence: Storing state to localStorage or sending to server
  4. 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

  1. Always prefer serializable data when possible
  2. Document any intentional non-serializable values
  3. Isolate non-serializable values to specific parts of your state
  4. Consider alternative state management for complex non-serializable state (React context, component state)

Leave a Reply

Your email address will not be published. Required fields are marked *