使用 Javascript/Lodash 替换 Redux Store 中对象数组中的对象

Replacing an object in an object array in Redux Store using Javascript/Lodash

我在 reducer 中有一个对象数组,如下所示:

[
  {id:1, name:Mark, email:mark@email.com}, 
  {id:2, name:Paul, email:paul@gmail.com}, 
  {id:3,name:sally, email:sally@email.com} 
]

下面是我的减速器。到目前为止,我可以通过以下方式向 currentPeople reducer 添加一个新对象:

const INITIAL_STATE = { currentPeople:[]};

export default function(state = INITIAL_STATE, action) {
  switch (action.type) {
    case ADD_PERSON:
      return {...state, currentPeople: [ ...state.currentPeople, action.payload]}; 
  }
  return state;
}

但这就是我卡住的地方。我可以使用 lodash 通过 reducer 更新一个人吗? 如果我发送一个看起来像这样的动作负载:

  {id:1, name:Eric, email:Eric@email.com}

我可以用新字段替换 ID 为 1 的对象吗?

这是数据规范化的一个很好的候选者。如果在将数据存储到状态树之前对其进行规范化,则可以有效地用新数据替换数据。

此示例直接来自 Normalizr

[{
  id: 1,
  title: 'Some Article',
  author: {
    id: 1,
    name: 'Dan'
  }
}, {
  id: 2,
  title: 'Other Article',
  author: {
    id: 1,
    name: 'Dan'
  }
}]

可以这样归一化-

{
  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'
      }
    }
  }
}

归一化有什么好处?

您可以提取您想要的状态树的确切部分。

例如-您有一个对象数组,其中包含有关文章的信息。如果您想 select 该数组中的特定对象,则必须遍历整个数组。最坏的情况是数组中不存在所需的对象。为了克服这个问题,我们将数据标准化。

要规范化数据,请将每个对象的唯一标识符存储在单独的数组中。我们将该数组称为 results.

result: [1, 2, 3 ..]

并将对象数组转换为具有键的对象作为id(参见第二个片段)。将该对象称为 entities.

最终,要使用 id 1 访问对象,只需执行此操作 - entities.articles["1"]

如果你想用新数据替换旧数据,你可以这样做-

entities.articles["1"] = newObj;

是的,您完全可以根据需要更新数组中的对象。如果你不想的话,你不需要改变你的数据结构。您可以将这样的案例添加到您的减速器中:

case UPDATE_PERSON: 
    return {
        ...state, 
        currentPeople: state.currentPeople.map(person => {
            if (person.id === action.payload.id) {
               return action.payload;
            }

            return person;
        }),
    };

这也可以缩短,使用隐式 returns 和三元:

case UPDATE_PERSON: 
    return {
        ...state, 
        currentPeople: state.currentPeople.map(person => (person.id === action.payload.id) ? action.payload : person),
    };

Mihir 关于使用 normalizr 将数据映射到对象的想法当然是可能的,从技术上讲,使用引用更新用户而不是执行循环(在完成初始映射后)会更快。但是如果你想保留你的数据结构,这种方法会奏效。

此外,像这样的映射只是更新对象的众多方法之一,并且需要浏览器支持 Array.prototype.map()。你可以使用 lodash indexOf() 来找到你想要的用户的索引(这很好,因为它在成功时打破了循环,而不是像 .map 那样继续),一旦你有了索引,你就可以覆盖对象直接使用它的索引。确保你没有改变 redux 状态,如果你想像这样分配,你需要在克隆上工作:clonedArray[foundIndex] = action.payload;.

使用数组原生拼接方式:

/*Find item index using lodash*/
var index = _.indexOf(currentPeople, _.find(currentPeople, {id: 1}));

/*Replace item at index using splice*/
arr.splice(index, 1, {id:1, name:'Mark', email:'mark@email.com'});