メインコンテンツにスキップ

Reducer ロジックの分割

ある程度の規模のアプリケーションでは、すべての更新ロジックを単一のreducer関数に配置すると、すぐに保守できなくなります。関数の長さに関する単一のルールはありませんが、一般的に関数は比較的短く、理想的には1つの特定の処理のみを行うべきであるとされています。このため、非常に長く、多くの異なる処理を行うコードを、理解しやすい小さな部分に分割することは、良いプログラミングプラクティスです。

Redux reducerは単なる関数であるため、同じ概念が適用されます。reducerロジックの一部を別の関数に分割し、親関数からその新しい関数を呼び出すことができます。

これらの新しい関数は、通常、次の3つのカテゴリのいずれかに分類されます。

  1. 複数の場所で必要となる、再利用可能なロジックの塊を含む小さなユーティリティ関数(特定のビジネスロジックに実際に関連している場合と関連していない場合があります)
  2. 特定の更新ケースを処理するための関数。多くの場合、通常の(state, action)ペア以外のパラメータが必要です。
  3. 状態の特定のスライスに対するすべての更新を処理する関数。これらの関数は、一般的に通常の(state, action)パラメータシグネチャを持ちます。

明確にするために、これらの用語を使用して、さまざまなタイプの関数とさまざまなユースケースを区別します。

  • reducer: (state, action) -> newState のシグネチャを持つ任意の関数(つまり、Array.prototype.reduceの引数として使用できる任意の関数)
  • ルートreducer: createStoreの最初の引数として実際に渡されるreducer関数。これは、(state, action) -> newStateシグネチャを持たなければならないreducerロジックの唯一の部分です。
  • スライスreducer: 状態ツリーの特定のスライスの更新を処理するために使用されているreducer。通常、combineReducersに渡すことによって行われます。
  • ケース関数: 特定のアクションの更新ロジックを処理するために使用されている関数。これは実際にはreducer関数である場合もあれば、適切に動作するために他のパラメータが必要な場合もあります。
  • 高階reducer: reducer関数を引数として受け取るか、結果として新しいreducer関数を返す関数(combineReducersredux-undoなど)

「* サブreducer *」という用語は、ルートreducerではない関数を意味するためにさまざまな議論で使用されてきましたが、この用語はあまり正確ではありません。一部の人々は、一部の関数を「* ビジネスロジック *」(アプリケーション固有の動作に関連する関数)または「* ユーティリティ関数 *」(アプリケーション固有ではない汎用関数)と呼ぶこともあります。

複雑なプロセスを、より小さく理解しやすい部分に分割することは、通常、関数分解 という用語で説明されます。この用語と概念は、あらゆるコードに一般的に適用できます。ただし、Reduxでは、更新ロジックが状態のスライスに基づいて他の関数に委任されるアプローチ#3を使用してreducerロジックを構造化することが非常に一般的です。Reduxはこの概念をreducer合成と呼び、reducerロジックを構造化するための最も広く使用されているアプローチです。実際、これは非常に一般的であるため、ReduxにはcombineReducers()と呼ばれるユーティリティ関数が含まれており、これは状態のスライスに基づいて他のreducer関数に作業を委任するプロセスを具体的に抽象化しています。ただし、これが使用できる唯一のパターンではないことに注意することが重要です。実際、ロジックを関数に分割するための3つのアプローチすべてを使用することは完全に可能であり、通常は良い考えでもあります。Reducerのリファクタリングセクションでは、実際に使用されている例をいくつか示しています。