【问题标题】:Replacing an object in an object array in Redux Store using Javascript/Lodash使用 Javascript/Lodash 在 Redux Store 中替换对象数组中的对象
【发布时间】:2016-12-24 22:04:05
【问题描述】:

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

[
  {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 通过减速器更新一个人吗? 如果我发送如下所示的操作负载:

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

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

【问题讨论】:

    标签: object reactjs redux store lodash


    【解决方案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'
          }
        }
      }
    }
    

    归一化有什么好处?

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

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

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

    result: [1, 2, 3 ..]

    并将对象数组转换为一个对象,其键为id(参见第二个sn-p)。将该对象称为entities

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

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

    entities.articles["1"] = newObj;

    【讨论】:

      【解决方案2】:

      是的,您完全可以按照自己的意愿更新数组中的对象。如果您不想更改数据结构,则无需更改。您可以在减速器中添加这样的案例:

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

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

      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;

      【讨论】:

      • 我选择了这个作为答案,因为我仍然需要这种格式并且它有效!只是一个简单的问题,我使用了你的映射示例,它工作得很好,但你会建议使用 lodash 代替,因为这需要浏览器支持?
      • 这真的取决于你需要支持什么浏览器。 IE9+ 以及 Chrome/SafariFF/Opera 都支持 Array.map。因此,除非您有特定的浏览器要求来支持 IE8,否则您应该可以使用 array.map。如需完整的浏览器支持和旧版浏览器的 polyfill,请参阅 MDN 页面:developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
      【解决方案3】:

      使用数组的原生拼接方法:

      /*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'});
      

      【讨论】:

      • 阅读本文的任何人都应该注意,在 Redux 中,.splice() 应该以一种不会改变状态但返回新状态的方式在数组的副本上完成。有关原因的更多信息:redux.js.org/docs/Troubleshooting.html
      猜你喜欢
      • 1970-01-01
      • 2016-10-20
      • 1970-01-01
      • 1970-01-01
      • 2019-10-20
      • 1970-01-01
      • 2016-08-06
      • 1970-01-01
      • 2019-04-18
      相关资源
      最近更新 更多