Redux Toolkitで「状態に非シリアル化可能な値が検出されました」エラーが発生する原因と解決策
Redux Toolkitを使用時に、「状態に非シリアル化可能な値が検出されました」(A non-serializable value was detected in the state) というエラーが発生する場合があります。これは、Redux Toolkitが状態スナップショットを保存する際に、一部の値がシリアル化できないことが原因です。
原因
このエラーが発生する主な原因は2つあります。
- 非シリアル化可能なオブジェクト
Redux Toolkitは状態スナップショットをJSON形式で保存するため、状態に非シリアル化可能なオブジェクトが含まれていると、このエラーが発生します。非シリアル化可能なオブジェクトとは、JSONに変換できないオブジェクトです。具体的には、以下のようなオブジェクトが該当します。
- 関数
- シンボル
- RegExp
- Dateオブジェクト
- 循環参照
状態オブジェクト内に循環参照があると、JSONに変換できないため、このエラーが発生します。循環参照とは、オブジェクトAがオブジェクトBを参照し、オブジェクトBがオブジェクトAを参照するような関係を指します。
解決策
このエラーを解決するには、以下の方法を試してください。
- 非シリアル化可能なオブジェクトをシリアル化可能な形式に変換する
非シリアル化可能なオブジェクトをシリアル化可能な形式に変換することで、エラーを解決できます。具体的には、以下のような方法があります。
- 関数を文字列に変換する
- シンボルを文字列に変換する
- RegExpを文字列に変換する
- Dateオブジェクトをタイムスタンプに変換する
- Mapオブジェクトをオブジェクトに変換する
- Setオブジェクトを配列に変換する
- 循環参照を解消する
循環参照を解消することで、エラーを解決できます。具体的には、以下のような方法があります。
- オブジェクトグラフを再構築する
- 参照関係を断ち切る
Redux Toolkitと通常のReduxの違い
Redux Toolkitは、Reduxをより簡単に使用できるようにするためのツールキットです。Redux Toolkitは、状態スナップショットを保存する際に、デフォルトでImmerを使用します。Immerは、状態オブジェクトをイミュータブルな方法で更新するためのライブラリです。
import { createSlice } from "@reduxjs/toolkit";
const initialState = {
date: new Date(),
};
const slice = createSlice({
name: "example",
initialState,
reducers: {
setDate(state, action) {
state.date = action.payload;
},
},
});
export const { actions, reducer } = slice;
// エラーが発生するコード
const store = createStore(reducer);
store.dispatch(actions.setDate(new Date()));
このコードでは、initialState
にDate
オブジェクトが含まれています。Date
オブジェクトは非シリアル化可能なオブジェクトなので、このコードを実行すると、「状態に非シリアル化可能な値が検出されました」エラーが発生します。
このエラーを解決するには、Date
オブジェクトをタイムスタンプに変換する必要があります。以下のコードは、Date
オブジェクトをタイムスタンプに変換する例です。
const initialState = {
date: new Date().getTime(),
};
const slice = createSlice({
name: "example",
initialState,
reducers: {
setDate(state, action) {
state.date = action.payload;
},
},
});
export const { actions, reducer } = slice;
// エラーが発生しないコード
const store = createStore(reducer);
store.dispatch(actions.setDate(new Date().getTime()));
このコードでは、initialState
にDate
オブジェクトのタイムスタンプが含まれています。タイムスタンプはシリアル化可能な値なので、このコードを実行すると、エラーは発生しません。
- 関数を状態に含める
- シンボルを状態に含める
- RegExpを状態に含める
- Mapオブジェクトを状態に含める
- 循環参照を含むオブジェクトを状態に含める
Redux Toolkitはデフォルトで immer
を使用していますが、immer
を使用せずに状態を管理することもできます。immer
を使用しない場合は、状態オブジェクトを直接更新する必要があります。
JSON.stringify を使用して状態をシリアル化する
Redux Toolkitはデフォルトで immer
を使用して状態スナップショットを保存しますが、JSON.stringify
を使用して状態をシリアル化することもできます。JSON.stringify
を使用する場合は、循環参照がないことを確認する必要があります。
状態スナップショットを保存しない
どうしても状態スナップショットを保存する必要がない場合は、保存しないという選択肢もあります。状態スナップショットを保存しない場合は、このエラーが発生することはありません。
カスタムシリアライザーを使用する
immer
や JSON.stringify
などのデフォルトのシリアライザーではなく、カスタムシリアライザーを使用することもできます。カスタムシリアライザーを使用することで、特定のオブジェクトをシリアル化可能な形式に変換することができます。
どの方法を選択するかは、状況によって異なります。 以下のような点を考慮して、適切な方法を選択する必要があります。
- 開発のしやすさ
- パフォーマンス
- メモリ使用量
javascript reactjs redux