Redux中的一个核心概念是,状态为immutable。但是,我看到了许多示例,包括Redux docs使用javascript Object.assign。然后,我在MDN中看到了这个警告:
对于深度克隆,我们需要使用其他替代方法,因为 Object.assign()复制属性值。如果源值是a 引用一个对象,它只复制该引用值。
那么,为什么使用Object.assign如果整点是不变的呢?我在这里错过了什么吗?
答案 0 :(得分:9)
让我们看看您链接的示例:
function todoApp(state = initialState, action) { switch (action.type) { case SET_VISIBILITY_FILTER: return Object.assign({}, state, { visibilityFilter: action.filter }) default: return state } }
是的,这是state
的浅层副本,创建了一个新对象,其中包含旧对象,但更新了visibilityFilter
。但是如果你对一致关于不可变地处理对象,那么如果新状态和旧状态共享对其他不可变对象的引用,那就没问题了。后来,大概是,如果你要改变别的东西,你就会遵循相同的模式。在这一点上,我们上面的浅拷贝将继续使用旧的,我的新对象将使用新的。
如果您一直向下应用不变性,那么您正在修改的级别的浅层副本及其所有父级别就是您所需要的。在上面的示例中,修改位于顶层,因此它只是一个副本。
但如果它更深入呢?让我们说你有这个对象:
let obj = {
a: {
a1: "a1 initial value",
a2: "a2 initial value"
},
b: {
b1: "b1 initial value",
b2: "b2 initial value"
}
};
如果您想更新a
,那就像上面的状态示例一样;您只需使用obj
的一个浅表副本即可完成此操作:
obj = Object.assign({}, obj, {a: {a1: "new a1", a2: "new a2"}});
或spread properties(目前处于第3阶段,通常在JSX转换程序设置中启用,可能会生成ES2018):
obj = {...obj, a: {a1: "new a1", a2: "new a2"}};
但是如果只是想要更新a1
怎么办?要做到这一点,您需要a
和 obj
的副本(因为如果您不复制obj
,则需要修改它引用的树,违反了委托人):
obj = Object.assign({}, obj, {a: Object.assign({}, obj.a, {a1: "updated a1"})});
或传播属性:
obj = {...obj, a: {...obj.a, a1: "updated a1"}};
答案 1 :(得分:2)
Redux只是一个数据存储。因此,在最纯粹意义上,redux并不真正需要不可变性或深度克隆作为一个概念。
但是,redux需要不变性才能与构建于其上的UI框架(如React)配合良好。
出于这个简单的原因:我的州的哪些部分在上次框架看起来之间发生了变化?
鉴于这个目标,你能看到深度克隆实际上没有帮助吗?如果你看一个已被深度克隆的对象,那么该对象的每个子部分现在在身份方面都是不同的(===
)。
作为一个具体示例,如果您运行以下代码:
const bookstore = { name: "Jane's books", numBooks: 42 };
const reduxData = { bookstore, employees: ['Ada', 'Bear'] };
现在让我们说你想改变你在书店的书籍数量。
如果您执行了深层复制,请执行以下操作:
const reduxClone = JSON.parse(JSON.stringify(reduxData));
reduxClone.bookstore.numBooks = 25;
然后你会发现书店和员工现在都不同了:
console.log(reduxData.bookstore === reduxClone.bookstore); // returns false
console.log(reduxData.employees === reduxClone.employees); // returns false, but we haven't changed the employees
这是一个问题,因为它似乎所有已更改。现在React必须重新渲染所有内容,看看是否有任何变化。
正确的解决方案是使用简单的不变性规则。如果更改对象的值,则必须创建该对象的新副本。所以,既然我们想要一个新的numBooks,我们需要创建一个新的书店。由于我们有一个新的书店,我们需要建立一个新的redux商店。
const newBookstore = Object.assign({}, bookstore, {numBooks: 25});
const shallowReduxClone = Object.assign({}, reduxData, {bookstore: newBookstore});
现在,你会看到书店已经改变了(耶!),但员工却没有(双倍!)
console.log(reduxData.bookstore === shallowReduxClone.bookstore); // returns false
console.log(reduxData.employees === shallowReduxClone.employees); // returns true
我希望这个例子有所帮助。 Immutability允许您在进行更改时更改最少量的对象。如果您保证永远不会更改对象,那么您可以在您构建的其他树中重用该对象。在这个例子中,我们能够使用employees
对象两次,没有危险,因为我们承诺永远不会改变employees对象。
答案 2 :(得分:1)
不变性意味着:我(作为开发人员)从不为对象属性分配新值;或者因为编译器不允许它,对象被冻结或我只是不这样做。相反,我创建了新对象。
如果你始终记住你不应该改变对象并遵守它,你就永远不会修改现有对象的内容,即使你事先浅层复制它而不是深层复制它(修改现有对象是什么不变的代码允许阻止,因为这使代码的行为更容易预测。)
因此,要创建不受影响的代码,您不需要深度复制。
为什么要避免深度复制?
不需要深层复制的无需代码的示例(在幕后,它使用Object.assign
):
const state = { firstName: 'please tell me', lastName: 'please tell me' }
const modifiedState = { ...state, firstName: 'Bob' }
当然,如果您使用浅层复制而不是深层复制对象,则可能会出错:
const state = { firstName: 'please tell me', lastName: 'please tell me' }
const modifiedState = state
modifiedState.firstName = 'Bob' // violates immuting, because it changes the original state as well