Skip to content

Redux Toolkitで「非シリアライズ可能な値が検出されました」エラーの解決方法

問題概要

Redux Toolkitの configureStore を使用すると、次のようなエラーが発生することがあります:

A non-serializable value was detected in the state, in the path: `varietals.red.0`. Value:, Varietal {
  "color": "red",
  "id": "2ada6486-b0b5-520e-b6ac-b91da6f1b901",
  "isCommon": true,
  "isSelected": false,
  "varietal": "bordeaux blend",
}

このエラーは、カスタムクラスのインスタンスや非シリアライズ可能な値がReduxストアの状態に含まれている場合に発生します。

根本的な原因

Reduxでは、状態のシリアライズ可能性が重要です。これは以下の理由によるものです:

  • デバッグの容易さ(Redux DevToolsでの状態の確認)
  • 状態の永続化(redux-persistなどの使用)
  • 時間旅行デバッグ
  • サーバーサイドレンダリング

クラスインスタンスは JSON.stringify() でシリアライズできないため、Reduxの状態に含めることは推奨されません。

解決方法

方法1: プレーンオブジェクトを使用する(推奨)

クラスインスタンスの代わりにプレーンな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,
  }));

方法2: シリアライズチェックの無効化(一時的な対策)

どうしても非シリアライズ可能な値が必要な場合は、シリアライズチェックを無効にすることができます:

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

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

注意

この方法は推奨されません。シリアライズチェックは重要なセーフティネットであり、無効にするとデバッグが難しくなる可能性があります。

方法3: 特定のアクションやパスのみを無視する

より制御された方法として、特定のアクションやパスだけを無視する設定も可能です:

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

redux-persistを使用している場合

redux-persistを使用している場合は、特定のアクションを無視リストに追加する必要があります:

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

const persistConfig = {
  key: 'root',
  storage,
};

const persistedReducer = persistReducer(persistConfig, rootReducer);

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

export const persistor = persistStore(store);

ベストプラクティス

  1. プレーンオブジェクトを使用する: Reduxの状態は常にプレーンなオブジェクトと配列で構成する
  2. シリアライズチェックを維持する: 可能な限りシリアライズチェックを有効に保つ
  3. 必要な場合のみ例外を設定する: どうしても必要な場合のみ、特定のパスやアクションを無視リストに追加する
  4. 非シリアライズ可能なデータを分離する: コンポーネントのローカル状態やコンテキストを使用する

まとめ

Redux Toolkitのシリアライズチェックは、状態の予測可能性とデバッグの容易さを確保するための重要な機能です。クラスインスタンスなどの非シリアライズ可能な値は避け、プレーンなJavaScriptオブジェクトを使用することが最良の解決策です。どうしても必要な場合のみ、制御された方法でシリアライズチェックを調整してください。