在redux project的todoMVC示例中,用于处理待办事项的reducer有以下几行:
export default function todos(state = initialState, action){
...
case EDIT_TODO:
return state.map(todo =>
todo.id === action.id ?
Object.assign({}, todo, { text: action.text }) :
todo
)
}
此部分代码涉及更新特定的待办事项。我的问题是,因为state.map()将始终返回一个新数组。然后,还有必要这样做:
Object.assign({}, todo, { text: action.text})
可以这样:
Object.assign(todo, { text: action.text})
我理解Object.assign({}, blah...)
与Object.assign(obj, blah...)
之间的区别。让我重新解释一下我的问题:
Redux希望reducer返回一个新状态而不是改变现有状态。我知道了。在我的例子中,我有一个对象数组。我想交换前两个元素的顺序。查看jsbin example here。
所以我的问题是第三个元素,我应该使用:
Object.assign({}, third_element)
或只是return the third_elment
Redux是否需要一个新的数组,其中包含对其中每个对象的新引用(即使存储在这些新对象中的值与旧对象相同),还是仅仅是一个只有新更新元素的新数组?
答案 0 :(得分:15)
这是Object.assign
的工作原理。它返回目标对象作为其操作的返回值。所以,在第一种语法中:
Object.assign({}, todo, { text: action.text})
您正在使用todo
和{ text: action.text}
的可枚举属性创建一个全新的对象。
如果你这样做:
Object.assign(todo, { text: action.text})
然后todo
本身就是目标对象,因此将会发生变异,这将是从Object.assign
返回的内容。
第一种方法是通过创建一个全新对象的新数组使地图操作完全不可变。
这是一个说明我的意思的jsbin。请注意,在第一个示例中,数组中的原始对象已更改,这意味着状态已发生变异。在第二个中,原始对象保持不变:
答案 1 :(得分:6)
来自Dan Abramov(Redux的创造者)的推文:
Common Redux误解:你需要深度克隆状态。现实:如果里面的东西没有改变,保持它的参考相同!
答案 2 :(得分:3)
我想补充一下这个很好的答案并解释为什么需要从Object.assign
返回一个新对象。
使用redux的工作方式,如果reducer返回state
参数中收到的相同引用,则redux将假定状态未更改并且不会更新视图。为了告诉redux你正在改变状态,你必须返回一个新的引用。这就是为什么您总会在减速器中看到一个新的空对象{}
作为Object.assign
的第一个参数。