問題は解決しましたが、特定の状況に大きく依存するため、正しい答えはないと思います。私の場合、次のアプローチを採用することにしました。
元のセレクターがうまく処理した課題の 1 つは、任意の順序で配信された多くの部分から最終的な情報がコンパイルされたことです。レデューサーで最終的な情報を段階的に構築することにした場合、考えられるすべてのシナリオ (情報が到着する可能性のあるすべての順序) を考慮し、考えられるすべての状態間の変換を定義する必要があります。一方、再選択では、現在持っているものを単純に取り出して、そこから何かを作ることができます。
この機能を維持するために、セレクター ロジックをラッピングする親 reducer に移動することにしました。
さて、A、B、C の 3 つのレデューサーと、対応するセレクターがあるとしましょう。それぞれが 1 つの情報を処理します。作品はサーバーからロードすることも、クライアント側のユーザーから発信することもできます。これは私の元のセレクターになります:
const makeFinalState(a, b, c) => (new List(a)).map(item =>
new MyRecord({ ...item, ...(b[item.id] || {}), ...(c[item.id] || {}) });
export const finalSelector = createSelector(
[selectorA, selectorB, selectorC],
(a, b, c) => makeFinalState(a, b, c,));
(これは実際のコードではありませんが、意味があることを願っています。個々のレデューサーの内容が利用可能になる順序に関係なく、セレクターは最終的に正しい出力を生成することに注意してください。)
私の問題が明確になったことを願っています。これらのレデューサーのいずれかのコンテンツが変更された場合、セレクターは最初から再計算され、すべてのレコードの完全に新しいインスタンスが生成され、最終的に React コンポーネントが完全に再レンダリングされます。
私の現在のソリューションはこれを軽視しています:
export default function finalReducer(state = new Map(), action) {
state = state
.update('a', a => aReducer(a, action))
.update('b', b => bReducer(b, action))
.update('c', c => cReducer(c, action));
switch (action.type) {
case HEAVY_ACTION_AFFECTING_A:
case HEAVY_ACTION_AFFECTING_B:
case HEAVY_ACTION_AFFECTING_C:
return state.update('final', final => (final || new List()).mergeDeep(
makeFinalState(state.get('a'), state.get('b'), state.get('c')));
case LIGHT_ACTION_AFFECTING_C:
const update = makeSmallIncrementalUpdate(state, action.payload);
return state.update('final', final => (final || new List()).mergeDeep(update))
}
}
export const finalSelector = state => state.final;
核となるアイデアは次のとおりです。
- 何か大きなことが起こった場合 (つまり、サーバーから大量のデータを取得した場合)、派生状態全体を再構築します。
- 何か小さなことが起こった場合 (つまり、ユーザーがアイテムを選択した場合) は、元のレデューサーとラッピングする親レデューサーの両方で、迅速な増分変更を行うだけです (一定の重複がありますが、一貫性と優れたパフォーマンスの両方を達成するために必要です)。
セレクター バージョンとの主な違いは、常に新しい状態を古い状態とマージすることです。Immutable.js ライブラリは、コンテンツが完全に同じ場合、古い Record インスタンスを新しい Record インスタンスに置き換えないほどスマートです。したがって、元のインスタンスが保持され、その結果、対応する純粋なコンポーネントは再レンダリングされません。
明らかに、ディープ マージはコストのかかる操作であるため、これは非常に大きなデータ セットでは機能しません。しかし、実際のところ、この種の操作は、React の再レンダリングや DOM 操作に比べて依然として高速です。したがって、このアプローチは、パフォーマンスとコードの読みやすさ/簡潔さの間の適切な妥協点になる可能性があります。
最後の注意: 個別に処理される軽いアクションがなければ、このアプローチは、純粋なコンポーネントの内部メソッドに置き換えることshallowEqual
と本質的に同等です。deepEqual
shouldComponentUpdate