查看自述文件中的示例:
鉴于“不良”结构:
[{ id: 1, title: 'Some Article', author: { id: 1, name: 'Dan' } }, { id: 2, title: 'Other Article', author: { id: 1, name: 'Dan' } }]
添加新对象非常容易。我要做的就是
return { ...state, myNewObject }
在减速器中。
现在给出“好”树的结构,我不知道该如何处理。
{ result: [1, 2], entities: { articles: { 1: { id: 1, title: 'Some Article', author: 1 }, 2: { id: 2, title: 'Other Article', author: 1 } }, users: { 1: { id: 1, name: 'Dan' } } } }
我想到的每种方法都需要进行一些复杂的对象操作,这使我感到自己处在不正确的轨道上,因为normalizr应该会使我的生活更轻松。
我无法在网上找到以这种方式使用normalizr树的任何示例。官方示例没有添加和删除,因此也没有帮助。
有人可以让我知道如何以正确的方式从normalizr树中添加/删除吗?
以下是直接从后通过了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相互引用。因此,只要某些对象发生更改,就只有一个地方需要更新。
其次,注意我们对CREATE_PLAN的反应,方法是在计划缩减器中添加适当的实体,并将其ID添加到currentPlans缩减器中。这个很重要。在更复杂的应用程序中,您可能会有关系,例如,计划缩减器可以通过将新ID附加到计划内部的数组来以相同方式处理ADD_EXERCISE_TO_PLAN。但是,如果演练本身进行了更新,则由于ID不变,因此计划缩减者无需知道这一点。
第三,请注意,实体化简(计划和练习)有特殊的条款,提请注意action.entities。如果我们有一个带有“已知事实”的服务器响应,我们想更新所有实体以反映出来。要在分派操作之前以这种方式准备数据,可以使用normalizr。您可以在Redux repo的“真实世界”示例中看到它。
最后,请注意实体化简器是如何相似的。您可能想编写一个函数来生成这些函数。这超出了我的答案范围,有时您需要更大的灵活性,有时您需要更少的样板。您可以在“真实世界”示例化例中查看分页代码,以生成类似的化例。
哦,我用了{… a,… b}语法。它已在Babel第二阶段作为ES7提案启用。它称为“对象散布运算符”,等效于编写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) };