如何在redux中更新特定数组项内的单个值

IT技术 javascript reactjs redux
2021-01-22 12:10:17

我有一个问题,重新渲染状态会导致 ui 问题,并建议仅更新我的减速器中的特定值以减少页面上的重新渲染量。

这是我所在州的例子

{
 name: "some name",
 subtitle: "some subtitle",
 contents: [
   {title: "some title", text: "some text"},
   {title: "some other title", text: "some other text"}
 ]
}

我目前正在像这样更新它

case 'SOME_ACTION':
   return { ...state, contents: action.payload }

其中action.payload是包含新值的整个数组。但现在我实际上只需要更新内容数组中第二项的文本,这样的事情就行不通了

case 'SOME_ACTION':
   return { ...state, contents[1].text: action.payload }

action.payload现在我需要更新的文本在哪里

6个回答

您可以使用map. 这是一个示例实现:

case 'SOME_ACTION':
   return { 
       ...state, 
       contents: state.contents.map(
           (content, i) => i === 1 ? {...content, text: action.payload}
                                   : content
       )
    }
我也是这样做的,我只是不确定这是否是一个好方法,因为 map 将返回一个新数组。有什么想法吗?
2021-03-19 12:10:17
@Ventura 是的,这很好,因为它为数组创建了一个新引用,并为要更新的对象(并且只有那个)创建了一个普通的新对象,这正是您想要的
2021-03-19 12:10:17
我认为这是最好的方法
2021-03-25 12:10:17
我也经常这样做,但是迭代所有元素而不是更新必要的索引在计算上似乎相对昂贵。
2021-04-06 12:10:17
这与内存成本无关。这是关于不通过进入指数循环来减慢您的应用程序
2021-04-12 12:10:17

你可以使用React Immutability helpers

import update from 'react-addons-update';

// ...    

case 'SOME_ACTION':
  return update(state, { 
    contents: { 
      1: {
        text: {$set: action.payload}
      }
    }
  });

虽然我想你可能会做更多这样的事情?

case 'SOME_ACTION':
  return update(state, { 
    contents: { 
      [action.id]: {
        text: {$set: action.payload}
      }
    }
  });
虽然包已移至kolodny/immutability-helper,所以现在是yarn add immutability-helperimport update from 'immutability-helper';
2021-03-16 12:10:17
我的情况完全相同,但是当我更新数组中元素之一中的对象时,更新后的值不会反映在 componentWillReceiveProps 中。我直接在 mapStateToProps 中使用内容,我们是否必须在 mapStateToProps 中使用其他内容才能反映?
2021-03-28 12:10:17
确实,我是否需要以某种方式在我的减速器中包含这些助手?
2021-04-12 12:10:17

聚会很晚,但这里有一个适用于每个索引值的通用解决方案。

  1. 您创建一个新数组并将其从旧数组扩展到index要更改的数组

  2. 添加所需的数据。

  3. 创建并传播一个新数组,从index您要更改的数组到数组末尾

let index=1;// probably action.payload.id
case 'SOME_ACTION':
   return { 
       ...state, 
       contents: [
          ...state.contents.slice(0,index),
          {title: "some other title", text: "some other text"},
         ...state.contents.slice(index+1)
         ]
    }

更新:

我做了一个小module来简化代码,所以你只需要调用一个函数:

case 'SOME_ACTION':
   return {
       ...state,
       contents: insertIntoArray(state.contents,index, {title: "some title", text: "some text"})
    }

有关更多示例,请查看存储库

函数签名:

insertIntoArray(originalArray,insertionIndex,newData)

编辑:还有适用于各种值的Immer.js库,它们也可以深度嵌套。

您不必在一行中完成所有操作:

case 'SOME_ACTION': {
  const newState = { ...state };
  newState.contents = 
    [
      newState.contents[0],
      {title: newState.contents[1].title, text: action.payload}
    ];
  return newState
};
你说得对,我做了一个快速修复。顺便说一句,数组是固定长度的吗?您希望修改的索引是否也是固定的?当前方法仅适用于小数组和固定索引。
2021-03-22 12:10:17
用 {} 包裹你的 case body,因为 const 是块范围的。
2021-04-05 12:10:17

我相信当你需要在你的 Redux 状态上进行这种操作时,spread 操作符是你的朋友,这个原则适用于所有孩子。

让我们假设这是您的状态:

const state = {
    houses: {
        gryffindor: {
          points: 15
        },
        ravenclaw: {
          points: 18
        },
        hufflepuff: {
          points: 7
        },
        slytherin: {
          points: 5
        }
    }
}

而你想给拉文克劳加3分

const key = "ravenclaw";
  return {
    ...state, // copy state
    houses: {
      ...state.houses, // copy houses
      [key]: {  // update one specific house (using Computed Property syntax)
        ...state.houses[key],  // copy that specific house's properties
        points: state.houses[key].points + 3   // update its `points` property
      }
    }
  }

通过使用扩展运算符,您可以只更新新状态,而其他所有内容均保持不变。

来自这篇精彩文章的示例,您可以找到几乎所有可能的选项以及很好的示例。

当 OP 想要更新数组时,这对我来说就像一个对象
2021-03-31 12:10:17
是的,并没有真正回答问题
2021-04-04 12:10:17