用另一个不改变状态的数组项替换数组项

Posted

技术标签:

【中文标题】用另一个不改变状态的数组项替换数组项【英文标题】:Replace array item with another one without mutating state 【发布时间】:2016-05-23 14:15:54 【问题描述】:

这是我的状态示例的外观:

const INITIAL_STATE = 
 contents: [ , , , etc.. ],
 meta: 

我需要能够并以某种方式替换内容数组中的项目,知道它的索引,我已经尝试过:

      return 
        ...state,
        contents: [
          ...state.contents[action.meta.index],
          
            content_type: 7,
            content_body: 
              album_artwork_url: action.payload.data.album.images[1].url,
              preview_url: action.payload.data.preview_url,
              title: action.payload.data.name,
              subtitle: action.payload.data.artists[0].name,
              spotify_link: action.payload.data.external_urls.spotify
            
          
        ]
      

其中action.meta.index 是我想用另一个内容对象替换的数组项的索引,但我相信这只是将整个数组替换为我正在传递的这个对象。我也想过使用.splice(),但这只会改变数组吗?

【问题讨论】:

另外,看看 React 的 Immutability 助手 - facebook.github.io/react/docs/update.html 它们并不像看起来那么难使用,而且确实让你的代码更容易阅读。 【参考方案1】:

请注意,Array.prototype.map() (docs) 确实不会改变原始数组,因此它提供了另一种选择:

 const INITIAL_STATE = 
   contents: [ , , , etc.. ],
   meta: 
 

 // Assuming this action object design
 
   type: MY_ACTION,
   data: 
     // new content to replace
   ,
   meta: 
     index: /* the array index in state */,
   
 

 function myReducer(state = INITIAL_STATE, action) 
   switch (action.type) 
     case MY_ACTION: 
       return 
         ...state,
         // optional 2nd arg in callback is the array index
         contents: state.contents.map((content, index) => 
           if (index === action.meta.index) 
             return action.data
           

           return content
         )
       
   
 

【讨论】:

绝对是最优雅的解决方案。【参考方案2】:

只是以@sapy 的正确答案为基础。我想向您展示另一个示例,说明如何在不改变状态的情况下更改 Redux 中数组内对象的属性。

我所在的州有一组orders。每个order 都是一个包含许多属性和值的对象。但是,我只想更改 note 属性。所以像这样的事情

let orders = [order1_Obj, order2_obj, order3_obj, order4_obj];

例如order3_obj = note: '', total: 50.50, items: 4, deliverDate: '07/26/2016';

所以在我的 Reducer 中,我有以下代码:

return Object.assign(, state,

  orders: 
    state.orders.slice(0, action.index)
    .concat([
      ...state.orders[action.index],
      notes: action.notes 
    ])
    .concat(state.orders.slice(action.index + 1))
   )

所以本质上,您正在执行以下操作:

1) 在order3_obj 之前切出数组,所以[order1_Obj, order2_obj]

2) 使用三个点... 扩展运算符和您要更改的特定属性(即note)连接(即添加)编辑的order3_obj

3) 在剩余的订单数组中使用.concat.slice 连接,最后.concat(state.orders.slice(action.index + 1))order3_obj 之后的所有内容(在这种情况下,order4_obj 是唯一剩下的一个)。

【讨论】:

非常感谢!超级有用的解释 这对我帮助很大。为这个【参考方案3】:

Splice 改变你需要使用的数组 Slice 。你还需要concat切片。

return Object.assign(, state,  
         contents:
          state.contents.slice(0,action.meta.index)
          .concat([
            content_type: 7,
            content_body: 
              album_artwork_url: action.payload.data.album.images[1].url,
              preview_url: action.payload.data.preview_url,
              title: action.payload.data.name,
              subtitle: action.payload.data.artists[0].name,
              spotify_link: action.payload.data.external_urls.spotify
            
          ])
          .concat(state.contents.slice(action.meta.index + 1))
  

【讨论】:

已编辑。我猜您正在尝试发送整个状态,内容值已更改。因此,使用 Object.assign 发送新的 state ,如果 Object.assign 中的同一键有多个来源,则最后一个来源获胜。

以上是关于用另一个不改变状态的数组项替换数组项的主要内容,如果未能解决你的问题,请参考以下文章

Vuex 状态下的重复数组项(使用 Socket.io)

如何在数组中的对象项上设置状态

Vue 列表项不会在状态更改时重新渲染

在reactjs中删除重复项后如何从状态值和setstate创建数组

如果 NaN 用另一列替换,Pandas 将组中的最后一项替换

替换数组项,源值也在公共静态无效[重复]上被替换