【问题标题】:Remove data from nested objects without mutating从嵌套对象中删除数据而不改变
【发布时间】:2016-05-22 09:46:58
【问题描述】:

有没有什么优雅的方法可以从数组中移除一个对象? 我已经使用 React 和 Redux 有一段时间了,但是每次我必须在不改变状态的情况下删除或插入数据时都会卡住几个小时。

reducer 是一个包含有 ID 的对象的数组和另一个包含对象的数组,如下所示:

[
 { id:123,
   items:[
           { id: abc,
             name: albert
           }, 
           ... 
         ]
 }, 
 ... 
]

我收到了两个 ID,需要删除 ID 为 abc 的项目。

【问题讨论】:

  • 您应该阅读这篇文章并重新考虑您的状态结构。 stackoverflow.com/questions/32135779/…
  • 不是将完整的复杂数组传递给reducer,而是创建一个新的reducer,接收数组的项目作为状态以进行更改
  • 嗯,这也是一种做法。但是,每次我需要 id:123 的对象的项目时,我都必须“搜索” dem。还有一个问题,当我从后端收到这样的 JSON 时,如何将其拆分为多个减速器?获取是通过一个动作完成的,该动作比一个减速器进行。

标签: javascript immutability redux mutation


【解决方案1】:

通过 id 从数组中删除一个项目:

return state.filter(item => item.id !== action.id)

通过 id 从对象中删除键:

let copy = Object.assign({}, state) // assuming you use Object.assign() polyfill!
delete copy[action.id] // shallowly mutating a shallow copy is fine
return copy

(奖励)与object spread operator proposal相同:

let { [action.id]: deletedItem, ...rest } = state
return rest

【讨论】:

  • 我最终使用了这些组合。
  • 最后一个例子中deletedItem 的作用/意思是/包含什么?
  • @Mbrevda deletedItem 只是一个变量名,它分配了已删除的项目(分配给 action.id 的项目)。您正在将您的状态转换为两个新对象,一个包含被拒绝的项目,另一个包含剩余的项目。
  • 如果您使用的是rest-spread,它目前不适用于数字键。在这种情况下,您必须使用[action.id.toString()]。查看问题github.com/babel/babel/issues/4196,以及建议的修复github.com/babel/babel/pull/3675
  • 如此优雅,我喜欢 {deletedItem,...rest} 的想法
【解决方案2】:
const remove = (state, bucketId, personId) => state.map(
  bucket => bucket.id === bucketId
    ? { ...bucket, items: bucket.items.filter(person => person.id !== personId) }
    : bucket,
);

用法:

const state = [
  {
    id: 123,
    items: [
      {
        id: 'abc',
        name: 'Kinna',
      },
      {
        id: 'def',
        name: 'Meggy',
      },
    ],
  },
  {
    id: 456,
    items: [
      {
        id: 'ghi',
        name: 'Ade',
      },
      {
        id: 'jkl',
        name: 'Tades',
      },
    ],
  },
];

console.log(remove(state, 123, 'abc'));

【讨论】:

    【解决方案3】:

    您可以使用Underscore's reject。它完全符合您的要求。

    【讨论】:

    • 返回数组而不是对象。
    【解决方案4】:

    如果你决定使用纯 Javascript,我能想到的最优雅的方法是使用 Array.prototype.reduce 来减少状态:

    var state = [
     { id: 123,
       items:[
               { id: 'abc',
                 name: 'albert'
               }, 
               ... 
             ]
     }, 
     ... 
    ]
    
    function filterOut (state) {
      return (bucketId, personId) => {
        return state.reduce((state, bucket) => {
          return state.concat(
            (bucketId === bucket.id) ?
              Object.assign({}, bucket, {items: bucket.items.filter((person) => person.id !== personId)}) :
              bucket
          );
        }, []);
      }
    }
    
    var newState = filterOut(state)(123, 'abc');
    

    【讨论】:

      【解决方案5】:

      您也可以使用lodash's omit method

      请注意,导入 lodash 会大大增加您的构建大小。通过仅导入特定方法来对其进行检查: import omit from 'lodash/omit';

      如果可能,我建议使用Dan's answer 中描述的对象扩展运算符。

      【讨论】:

        【解决方案6】:

        我用这种方式解决了我的问题

        if(action.type === "REMOVE_FROM_PLAYLIST"){
                let copy = Object.assign({}, state) 
                delete copy.playlist[action.index].songs[action.indexSongs];
        
                return copy;
            }
        

        希望它对其他人有所帮助。

        【讨论】:

          猜你喜欢
          • 2016-11-24
          • 1970-01-01
          • 1970-01-01
          • 2020-12-07
          • 2021-12-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-08-02
          相关资源
          最近更新 更多