这是使用 redux 删除项目的正确方法吗?
Posted
技术标签:
【中文标题】这是使用 redux 删除项目的正确方法吗?【英文标题】:Is this the correct way to delete an item using redux? 【发布时间】:2016-04-07 13:34:26 【问题描述】:我知道我不应该改变输入并且应该克隆对象来改变它。我遵循了 redux 入门项目中使用的约定:
ADD_ITEM: (state, action) => (
...state,
items: [...state.items, action.payload.value],
lastUpdated: action.payload.date
)
用于添加项目 - 我使用 spread 将项目附加到数组中。
删除我使用的:
DELETE_ITEM: (state, action) => (
...state,
items: [...state.items.splice(0, action.payload), ...state.items.splice(1)],
lastUpdated: Date.now()
)
但这会改变输入状态对象 - 即使我返回一个新对象,这是否被禁止?
【问题讨论】:
快速提问。 Splice 返回您删除的项目。这是你的意图吗?如果不是,你应该使用切片,它也遵守无突变法则。 好吧,在这个例子中,我将数组的两个部分拼接成一个新数组——我想删除的项目被遗漏了。 Slice 也返回删除的项目,对吗?只有它在不改变原始数组的情况下这样做,这样会是更好的方法吗? @AR7 根据您的建议:items: [...state.items.slice(0, action.payload.value), ...state.items.slice(action.payload.value + 1 )]
现在使用 slice 而不是 splice 以免改变输入 - 这是要走的路还是有更简洁的方法?
【参考方案1】:
没有。永远不要改变你的状态。
即使您返回一个新对象,您仍然会污染旧对象,这是您永远不想做的。这使得在新旧状态之间进行比较时会出现问题。例如在 shouldComponentUpdate
中 react-redux 在后台使用。它还使时间旅行变得不可能(即撤消和重做)。
相反,使用不可变的方法。始终使用Array#slice
,从不使用Array#splice
。
我从您的代码中假设action.payload
是被删除项目的索引。更好的方法如下:
items: [
...state.items.slice(0, action.payload),
...state.items.slice(action.payload + 1)
],
【讨论】:
如果我们正在处理数组中的最后一个元素,这将不起作用,并且在第二个语句中使用...
将使您的状态内容加倍
请用 jsfiddle/codepen 示例证明这一点。无论arr
的内容是什么,sn-p arr.slice(arr.length)
都应该始终生成一个空数组。
@david-l-walsh 对不起,我在测试这个例子时一定是打错字了。它创造了奇迹。我唯一的问题是:为什么第二部分需要扩展运算符 ...
- ...state.items.slice(action.payload + 1)
Array#slice
返回一个数组。要将两个切片组合成一个数组,我使用了扩展运算符。没有它,您将拥有一个数组数组。
这是有道理的。非常感谢您的澄清(对于最初的困惑,我们深表歉意)。【参考方案2】:
您可以使用数组过滤器方法从数组中删除特定元素,而不会改变原始状态。
return state.filter(element => element !== action.payload);
在您的代码上下文中,它看起来像这样:
DELETE_ITEM: (state, action) => (
...state,
items: state.items.filter(item => item !== action.payload),
lastUpdated: Date.now()
)
【讨论】:
过滤器是否产生一个新数组? @chenop 是的,Array.filter 方法返回一个新数组。 developer.mozilla.org/en-US/docs/Web/javascript/Reference/… 请注意,如果有重复,这将删除所有的。要使用过滤器删除特定索引,您可以使用例如arr.filter((val, i) => i !== action.payload )
【参考方案3】:
ES6 Array.prototype.filter
方法返回一个新数组,其中包含符合条件的项。因此,在原始问题的上下文中,这将是:
DELETE_ITEM: (state, action) => (
...state,
items: state.items.filter(item => action.payload !== item),
lastUpdated: Date.now()
)
【讨论】:
.filter()
不是 ES2015 的方法,但在之前的 ES5 版本中已经添加。【参考方案4】:
带有对象的数组的不可变“DELETED”reducer 的另一种变体:
const index = state.map(item => item.name).indexOf(action.name);
const stateTemp = [
...state.slice(0, index),
...state.slice(index + 1)
];
return stateTemp;
【讨论】:
【参考方案5】:黄金法则是我们不会返回一个变异的状态,而是一个新的状态。根据您的操作类型,您可能需要在遇到减速器时以各种形式更新您的状态树。
在这种情况下,我们试图从状态属性中删除一个项目。
这让我们想到了 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
【讨论】:
【参考方案6】:使用 redux 以不同方式删除项目。
方法1:在这种情况下使用createSlice(..)
const id = action.payload; // destruct id
removeCart: (state, action) =>
let id = action.payload;
let arr = state.carts.filter(item => item.id !== parseInt(id))
state.carts = arr;
方法2:在这种情况下使用 switch (... ), spread-operator
const id = action.payload; // destruct id
case actionTypes.DELETE_CART:
return
...state,
carts: state.carts.filter((item) => item.id !== payload)
;
对于这两种方法都初始化了这个状态:
initialState:
carts: ProductData, // in productData mocked somedata
【讨论】:
以上是关于这是使用 redux 删除项目的正确方法吗?的主要内容,如果未能解决你的问题,请参考以下文章
Redux Dispatch - 从数组/不正确的数组长度输出中删除项目