更改数组项目的最佳方法是什么?

时间:2016-11-15 06:07:44

标签: redux immutability

在Redux中更改商店数组元素的最佳方法是什么?我有这3个工作解决方案。哪个是最好的?我认为前两个解决方案正在改变状态。这是真的吗? 如果你有更好的解决方案,我会很感激。

在这个例子中我有一个订单列表,我想增加列表中一个订单的数量。我将此订单索引作为orderIndex传递。

第一种方式:

let order = state[action.orderIndex];
order.quantity++;
return Object.assign([],order);
第二种方式:

let order= Object.assign([],state);
order[action.orderIndex].quantity++;
return Object.assign([],order);
第三种方式:

return Object.assign([],state.map(
            (order,index) => (index==action.orderIndex) ? {...order,quantity:order.quantity+1} : order)
        );

我有另一个解决方案,但它返回一个对象,我不知道如何在此解决方案中返回一个数组。 :

            return {...state,[action.orderIndex]:{...state[action.orderIndex],quantity:state[action.orderIndex].quantity+1}};

2 个答案:

答案 0 :(得分:10)

如果您要修改数组中的项目,请使用新值创建一个新项目,然后返回一个新数组,将要修改的元素前后的元素切片,并将其插入中间。使用ES6扩展语法,您可以执行以下操作:

var newArray = [
  ...originalArray.slice(0, indexOfAmendedItem),
  amendedItem,
  ...originalArray.slice(indexOfAmendedItem + 1)
];

注意:

https://egghead.io/lessons/javascript-redux-avoiding-array-mutations-with-concat-slice-and-spread

它解释得非常出色。

答案 1 :(得分:5)

第一个修改原始顺序,即突变。这违反了Redux商店的不变性。

第二个生成数组的副本,但数组中的对象仍然是原始顺序。它们只是引用与原始数组相同的实例。因此,当您在下一行修改其中一个时,您正在改变Redux状态。

第三种方式是有趣的,因为即使你没有改变状态,它也可以简化。

return state.map(order => (index==action.orderIndex) ? {...order, quantity: order.quantity+1} : order);

由于map函数已经返回一个新数组,因此无需调用Object.assign

第三个版本有效,因为您引用所有未修改的订单,并仅为您要更改的项目创建新订单。由于不允许变异,因此您需要创建一个新对象。

现在,ES6语法可能令人困惑。您可以完全使用它而不使用ES6({1}除外,但您可以用下划线替换Object.assign。重要的是没有使用新语法。)

extend

或者,您可以直接修改副本,如下所示:

// copy the list so we can modify the copy without mutating the original
// it is important to realize that even though the list is brand new
// the items inside are still the original orders
var newOrders = state.slice();
// grab the old order we'd like to modify
// note that we could have grabbed it from state as well
// they are one and the same object
var oldOrder = newOrders[action.orderIndex];
// this takes a new object and copies all properties of oldOrder into it
// it then copies all properties of the final object into it
// the final object has only our new property
// we could have multiple properties there if we needed
var newOrder = Object.assign({}, oldOrder, { quantity: oldOrder.quantity + 1 });
// now we just replace the old order with the new one
// remember it's okay to modify the list since we copied it first
newOrders[action.orderIndex] = newOrder;
return newOrders;

两种做法之间的区别在于使用var newOrders = state.slice(); var oldOrder = newOrders[action.orderIndex]; // make a shallow copy var newOrder = Object.assign({}, oldOrder); // we can now modify immediate properties of the copy newOrder.quantity++; newOrders[action.orderIndex] = newOrder; return newOrders; 的版本(以及更多ES6重型版本)是单个表达式。这使它更具功能性。而不是指定获取结果的步骤,而是指定结果应该是什么样子。这是声明与命令。

声明版本有时更清晰。然后,通常它不是。 通常更短,但看起来很陌生。如果你来自功能世界,它的外观和感觉完全正常。外星人从不认为自己是外星人。

我建议理解的简便性比较短的代码更重要。请注意,它不可能更快。例如,map版本会为每个订单调用您的函数。

如果你有几百万个订单(我们都希望得到),我们最终会检查每个订单,看看它是否是我们想要修改的订单。

但是,在使用命令式版本时,您会复制一个数组,这是一个我们不检查任何订单的操作。它比map版本快得多,速度也快得多。

那么为什么我们会看到人们使用map呢?好吧,如果你只有几个订单,那么性能就不是问题了,对那些功能纯粹主义者来说看起来很好。

还有Mark对ES6做事方式的回答。它通过在要替换的项目之前复制零件,添加新项目,然后在要替换的项目之后添加零件来创建新阵列。这很快。它也很丑陋(在我看来)。

当然,如果你仔细想想它会发生什么。它取代了我们需要改变的一个项目。但是,它不像一个替代品。事实上,索引没有提到的唯一项目就是我们要替换的项目。

我想说,除非你试图证明你的整个应用程序只能是一个单一的表达式,否则编写命令式代码是完全可以的。如果它更容易理解或写,那么没有理由不这样做。