【问题标题】:Is this the correct way to delete an item using redux?这是使用 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 以免改变输入 - 这是要走的路还是有更简洁的方法?

标签: javascript reactjs redux


【解决方案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     
          }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-09-30
        • 1970-01-01
        • 2012-05-11
        • 2018-12-31
        • 2021-04-29
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多