以下は、redux/normalizr 作成者の投稿から直接引用したものです。
したがって、状態は次のようになります。
{
entities: {
plans: {
1: {title: 'A', exercises: [1, 2, 3]},
2: {title: 'B', exercises: [5, 1, 2]}
},
exercises: {
1: {title: 'exe1'},
2: {title: 'exe2'},
3: {title: 'exe3'}
}
},
currentPlans: [1, 2]
}
レデューサーは次のようになります
import merge from 'lodash/object/merge';
const exercises = (state = {}, action) => {
switch (action.type) {
case 'CREATE_EXERCISE':
return {
...state,
[action.id]: {
...action.exercise
}
};
case 'UPDATE_EXERCISE':
return {
...state,
[action.id]: {
...state[action.id],
...action.exercise
}
};
default:
if (action.entities && action.entities.exercises) {
return merge({}, state, action.entities.exercises);
}
return state;
}
}
const plans = (state = {}, action) => {
switch (action.type) {
case 'CREATE_PLAN':
return {
...state,
[action.id]: {
...action.plan
}
};
case 'UPDATE_PLAN':
return {
...state,
[action.id]: {
...state[action.id],
...action.plan
}
};
default:
if (action.entities && action.entities.plans) {
return merge({}, state, action.entities.plans);
}
return state;
}
}
const entities = combineReducers({
plans,
exercises
});
const currentPlans = (state = [], action) {
switch (action.type) {
case 'CREATE_PLAN':
return [...state, action.id];
default:
return state;
}
}
const reducer = combineReducers({
entities,
currentPlans
});
それで、ここで何が起こっているのですか?まず、状態が正規化されていることに注意してください。他のエンティティ内にエンティティが存在することはありません。代わりに、ID によって相互に参照します。そのため、オブジェクトが変更されるたびに、更新が必要な場所が 1 か所だけになります。
次に、plans レデューサーに適切なエンティティを追加し、その ID を currentPlans レデューサーに追加することで、CREATE_PLAN にどのように反応するかに注意してください。これは重要。より複雑なアプリでは、リレーションシップがある場合があります。たとえば、プラン レデューサーは、プラン内の配列に新しい ID を追加することで、同じ方法で ADD_EXERCISE_TO_PLAN を処理できます。ただし、演習自体が更新された場合、ID が変更されていないため、plans reducer がそれを知る必要はありません。
第 3 に、エンティティ リデューサー (計画と演習) には、action.entities を監視する特別な節があることに注意してください。これは、すべてのエンティティを更新して反映させたい「既知の真実」を含むサーバー応答がある場合です。アクションをディスパッチする前にこの方法でデータを準備するには、normalizr を使用できます。Redux リポジトリの「実世界」の例で使用されていることがわかります。
最後に、エンティティ レデューサーがどのように似ているかに注意してください。それらを生成する関数を書きたいと思うかもしれません。それは私の回答の範囲外です。柔軟性を高めたい場合もあれば、ボイラープレートを少なくしたい場合もあります。同様のレデューサーを生成する例については、「実際の」レデューサーの例でページネーション コードを確認できます。
ああ、私は { ...a, ...b } 構文を使用しました。ES7 プロポーザルとして、Babel ステージ 2 で有効になります。これは「オブジェクト拡散演算子」と呼ばれ、Object.assign({}, a, b) と書くのと同じです。
ライブラリに関しては、Lodash (変更しないように注意してください。たとえば、merge({}, a, b} は正しいが、merge(a, b) は正しくありません)、updeep、react-addons-update などを使用できます。ただし、深い更新を行う必要がある場合は、おそらく状態ツリーが十分にフラットではなく、機能構成を十分に活用していないことを意味します.最初の例でも:
case 'UPDATE_PLAN':
return {
...state,
plans: [
...state.plans.slice(0, action.idx),
Object.assign({}, state.plans[action.idx], action.plan),
...state.plans.slice(action.idx + 1)
]
};
次のように書くことができます
const plan = (state = {}, action) => {
switch (action.type) {
case 'UPDATE_PLAN':
return Object.assign({}, state, action.plan);
default:
return state;
}
}
const plans = (state = [], action) => {
if (typeof action.idx === 'undefined') {
return state;
}
return [
...state.slice(0, action.idx),
plan(state[action.idx], action),
...state.slice(action.idx + 1)
];
};
// somewhere
case 'UPDATE_PLAN':
return {
...state,
plans: plans(state.plans, action)
};