这是使用 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 - 从数组/不正确的数组长度输出中删除项目

Redux/Flux(使用 ReactJS)和动画

这是从iPhone删除联系人的正确方法吗?

如何使用 react redux 正确调度 axios 删除

我应该在 Redux 中存储 Promise 吗?

如何正确删除操作?