这是使用Redux删除项目的正确方法吗?


115

我知道我不应该改变输入,而应该克隆对象来改变它。我遵循在redux入门项目上使用的约定,该约定使用:

ADD_ITEM: (state, action) => ({
  ...state,
  items: [...state.items, action.payload.value],
  lastUpdated: action.payload.date
})

用于添加项目-我使用了点差将项目追加到数组中。

用于删除我使用的:

DELETE_ITEM: (state, action) => ({
  ...state,
  items: [...state.items.splice(0, action.payload), ...state.items.splice(1)],
  lastUpdated: Date.now() 
})

但这改变了输入状态对象-即使我要返回一个新对象,也禁止这样做吗?


1
快速提问。拼接返回您删除的项目。那是你的意图吗?如果不是,则应使用切片,该切片也应遵守无突变法。
m0meni

在此示例中,我将数组的两个部分拼接在一起形成一个新数组-我想删除的项被遗漏了。切片还返回删除的项目,对不对?只有这样做而不改变原始数组,这样才是更好的方法吗?
CWright

根据您的建议,items: [...state.items.slice(0, action.payload.value), ...state.items.slice(action.payload.value + 1 )]使用@ AR7: 现在使用slice而不是splice以便不使输入发生变化-这是可行的方法还是更简洁的方法?
CWright

Answers:


206

不,永远不要改变你的状态。

即使您要返回一个新对象,您仍然会污染旧对象,而您永远都不想这样做。这使得在旧状态与新状态之间进行比较时会出现问题。例如shouldComponentUpdate,react-redux在引擎盖下使用。这也使时间旅行成为不可能(即撤消和重做)。

而是使用不可变方法。永远使用Array#slice,永不Array#splice

我从您的代码中假定这action.payload是要删除的项目的索引。更好的方法如下:

items: [
    ...state.items.slice(0, action.payload),
    ...state.items.slice(action.payload + 1)
],

如果我们正在处理数组中的最后一个元素,这将不起作用,而且...在第二条语句中的使用将使状态的内容加倍
Thaenor

4
请使用jsfiddle / codepen示例进行证明。arr.slice(arr.length)无论的内容如何,该代码段均应始终生成一个空数组arr
David L. Walsh

1
@ david-l-walsh抱歉,我很困惑,在测试此示例时,我一定打错了字。它创造奇迹。我唯一的问题是:为什么...第二部分需要传播操作员...state.items.slice(action.payload + 1)
Thaenor

5
Array#slice返回一个数组。为了将两个切片组合成一个数组,我使用了散布运算符。没有它,您将拥有一个数组数组。
David L. Walsh

4
这就说得通了。非常感谢您的澄清(对不起,一开始很困惑)。
Thaenor

149

您可以使用数组过滤器方法从数组中删除特定元素,而无需更改原始状态。

return state.filter(element => element !== action.payload);

在您的代码上下文中,它看起来像这样:

DELETE_ITEM: (state, action) => ({
  ...state,
  items: state.items.filter(item => item !== action.payload),
  lastUpdated: Date.now() 
})

1
过滤器会产生新的数组吗?
chenop

6
@chenop是的,Array.filter方法返回一个新数组。developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
斯蒂芬中号

4
请注意,如果有重复项,将删除所有重复项。要使用过滤器删除特定索引,您可以使用arr.filter((val, i) => i !== action.payload )
-erich2k8(

21

ES6 Array.prototype.filter方法返回一个新数组,其中包含与条件匹配的项目。因此,在原始问题的范围内,这将是:

DELETE_ITEM: (state, action) => ({
  ...state,
  items: state.items.filter(item => action.payload !== item),
  lastUpdated: Date.now() 
})

.filter()不是ES2015方法,但已在早期版本ES5中添加。
莫尔克罗(Morkro),

7

带有对象的数组的不可变“ DELETED” reduce的另一个变体:

const index = state.map(item => item.name).indexOf(action.name);
const stateTemp = [
  ...state.slice(0, index),
  ...state.slice(index + 1)
];
return stateTemp;

0

黄金法则是我们不返回突变状态,而是返回新状态。根据操作的类型,当状态树命中reducer时,您可能需要以各种形式更新状态树。

在这种情况下,我们试图从状态属性中删除一个项目。

这使我们想到了Redux的不变更新(或数据修改)模式的概念。不变性是关键,因为我们永远不想直接更改状态树中的值,而总是复制并根据旧值返回新值。

这是有关如何删除嵌套对象的示例:

// ducks/outfits (Parent)

// types
export const NAME = `@outfitsData`;
export const REMOVE_FILTER = `${NAME}/REMOVE_FILTER`;

// initialization
const initialState = {
  isInitiallyLoaded: false,
  outfits: ['Outfit.1', 'Outfit.2'],
  filters: {
    brand: [],
    colour: [],
  },
  error: '',
};

// action creators
export function removeFilter({ field, index }) {
  return {
    type: REMOVE_FILTER,
    field,
    index,
  };
}

export default function reducer(state = initialState, action = {}) {
  sswitch (action.type) {  
  case REMOVE_FILTER:
  return {
    ...state,
    filters: {
    ...state.filters,
       [action.field]: [...state.filters[action.field]]
       .filter((x, index) => index !== action.index)
    },
  };
  default:
     return state;
  }
}

为了更好地理解这一点,请确保查看以下文章:https : //medium.com/better-programming/deleting-an-item-in-a-nested-redux-state-3de0cb3943da

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.