Skip to content

Redux Toolkit 检测到状态中的非可序列化值

问题描述

在使用 Redux Toolkit 的 configureStore 替换传统的 createStore 时,经常会遇到以下错误提示:

A non-serializable value was detected in the state, in the path: `varietals.red.0`.

这个错误通常发生在 Redux 状态中包含不可序列化的值时,比如类实例、函数、Promise 或其他复杂对象。错误信息会明确指出问题所在的路径和具体值。

根本原因

Redux 的核心原则之一是状态应该是可序列化的,这意味着状态应该能够被转换为 JSON 格式。Redux Toolkit 默认包含了序列化检查中间件,它会自动检测并警告状态或操作中的非可序列化值。

常见的不可序列化值包括:

  • 类实例(如自定义模型)
  • 函数和 Promise 对象
  • 日期对象(未转换为字符串时)
  • Map、Set 等特殊数据结构

解决方案

1. 最佳实践:使用纯 JavaScript 对象(推荐)

将类实例转换为纯 JavaScript 对象是最彻底的解决方案:

javascript
// 不推荐:使用类实例
class Varietal {
  constructor(id, color, varietal, isSelected, isCommon) {
    this.id = id;
    this.color = color;
    this.varietal = varietal;
    this.isSelected = isSelected;
    this.isCommon = isCommon;
  }
}

// 推荐:使用纯对象
export const createVarietalArray = (arr, color, isCommon) =>
  arr.map(v => ({
    id: uuidv5(v, NAMESPACE),
    color,
    varietal: v,
    isSelected: false,
    isCommon,
  }));

为什么推荐这种做法?

  • 符合 Redux 设计原则
  • 确保状态的完全可序列化
  • 提高应用的可预测性和调试便利性
  • 避免潜在的兼容性问题

2. 自定义序列化检查(临时解决方案)

如果确实需要在状态中包含某些不可序列化的值,可以配置中间件忽略特定路径:

javascript
import { configureStore } from '@reduxjs/toolkit';

const store = configureStore({
  reducer: rootReducer,
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware({
      serializableCheck: {
        ignoredActions: ['some/action/type'],
        ignoredActionPaths: ['meta.arg', 'payload.timestamp'],
        ignoredPaths: ['items.dates'],
      },
    }),
});

注意事项

这种方法应该谨慎使用,仅作为临时解决方案。长期来看,仍然建议重构代码以使用可序列化的数据。

3. 完全禁用序列化检查(不推荐)

在某些特殊情况下,可以完全禁用序列化检查:

javascript
const store = configureStore({
  reducer: rootReducer,
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware({
      serializableCheck: false,
    }),
});

警告

完全禁用序列化检查会失去 Redux Toolkit 提供的重要保护机制,可能导致难以调试的问题。只有在充分了解后果的情况下才应考虑此选项。

4. 使用 Redux Persist 时的特殊处理

当与 Redux Persist 一起使用时,需要忽略特定的 action 类型:

javascript
import {
  persistStore,
  persistReducer,
  FLUSH,
  REHYDRATE,
  PAUSE,
  PERSIST,
  PURGE,
  REGISTER,
} from 'redux-persist';

const store = configureStore({
  reducer: persistedReducer,
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware({
      serializableCheck: {
        ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
      },
    }),
});

实践建议

  1. 优先使用纯数据结构:避免在 Redux 状态中使用类实例,转而使用简单的对象和数组
  2. 保持状态简洁:只存储必要的 UI 状态,复杂逻辑应该在组件或选择器中处理
  3. 利用 Redux Toolkit 的优势:充分利用其内置的不可变更新逻辑和开发工具
  4. 逐步重构:如果现有代码使用了类实例,可以逐步将其替换为纯对象

总结

Redux Toolkit 的序列化检查是一个有价值的功能,它帮助开发者遵循 Redux 的最佳实践。虽然提供了禁用或配置这些检查的方法,但最推荐的解决方案仍然是保持状态的完全可序列化。通过使用纯 JavaScript 对象而不是类实例,可以确保应用的可维护性和稳定性。