Reducerの前提となる概念
「Redux Fundamentals」パート3:状態、アクション、Reducerで説明されているように、Reduxのreducer関数は
(previousState, action) => newState
のシグネチャを持つ必要があります。これは、Array.prototype.reduce(reducer, ?initialValue)
に渡す関数の型に似ています。- 「純粋」である必要があります。つまり、reducerは
- 副作用を実行しません(APIの呼び出しや、非ローカルオブジェクトまたは変数の変更など)。
- 非純粋関数を呼び出しません(`Date.now` や `Math.random` など)。
- 引数を変更しません。reducerが状態を更新する場合、**既存の**状態オブジェクトを直接変更するべきではありません。代わりに、必要な変更を含む**新しい**オブジェクトを生成する必要があります。reducerが更新する状態内のサブオブジェクトについても、同じアプローチを使用する必要があります。
イミュータビリティ、副作用、および変更に関する注意
変更は一般的にタイムトラベルデバッグを壊してしまうため推奨されません。また、React Reduxの`connect`関数は
- タイムトラベルの場合、Redux DevToolsは記録されたアクションを再生すると状態値が出力されることを期待しますが、それ以外は何も変更されません。**変更や非同期動作などの副作用は、ステップ間の動作を変化させ、アプリケーションを壊してしまうため、タイムトラベルに悪影響を及ぼします**。
- React Reduxの場合、`connect` は `mapStateToProps` 関数から返されたpropsが変更されたかどうかを確認して、コンポーネントを更新する必要があるかどうかを判断します。パフォーマンスを向上させるために、`connect` は状態がイミュータブルであることに依存するいくつかのショートカットを採用し、浅い参照等価チェックを使用して変更を検出します。これは、**直接変更によってオブジェクトと配列に加えられた変更は検出されず、コンポーネントは再レンダリングされない**ことを意味します。
reducerで一意のIDやタイムスタンプを生成するなどの他の副作用も、コードを予測不可能にし、デバッグとテストを困難にします。
これらのルールのため、Redux reducerを編成するための他の具体的なテクニックに進む前に、以下のコアコンセプトを十分に理解することが重要です。
Redux Reducerの基本
主要な概念:
- 状態と状態の形状について考える
- 状態のスライスによる更新責任の委任(*reducerの合成*)
- 高階reducer
- reducerの初期状態の定義
読書リスト:
- 「Redux Fundamentals」パート3:状態、アクション、Reducer
- Reduxドキュメント:ボイラープレートの削減
- Reduxドキュメント:アンドゥ履歴の実装
- Reduxドキュメント:`combineReducers`
- 高階reducerの力
- Stack Overflow:ストアの初期状態と `combineReducers`
- Stack Overflow:状態キー名と `combineReducers`
純粋関数と副作用
主要な概念:
- 副作用
- 純粋関数
- 関数の組み合わせ方について考える
読書リスト:
イミュータブルデータ管理
主要な概念:
- ミュータビリティ vs イミュータビリティ
- オブジェクトと配列を安全にイミュータブルに更新する
- 状態を変更する関数とステートメントを避ける
読書リスト:
データの正規化
主要な概念:
- データベースの構造と構成
- リレーショナル/ネストされたデータを別々のテーブルに分割する
- 特定のアイテムの定義を1つだけ格納する
- IDでアイテムを参照する
- アイテムIDをキーとするオブジェクトをルックアップテーブルとして使用し、IDの配列で順序を追跡する
- リレーションシップ内のアイテムを関連付ける
読書リスト: