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

问题描述 投票:6回答:2

在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}};
redux immutability
2个回答
5
投票

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

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

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

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

因为map函数已经返回一个新数组,所以不需要调用Object.assign

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

现在,ES6语法可能令人困惑。你可以让它完全没有ES6(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;

两种做法之间的区别在于使用map的版本(以及更多ES6重型版本)是单个表达式。这使它更具功能性。而不是指定获取结果的步骤,而是指定结果应该是什么样子。这是声明与命令。

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

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

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

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

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

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

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

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


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

它解释得非常出色。

© www.soinside.com 2019 - 2024. All rights reserved.