如果我以以下方式编写我的reducer,则将调用“ render”方法,并且这是预期的行为。没问题:
const initState = {
entries: ["test1"]
};
export const EntryReducer = (state = initState, action) => {
switch (action.type) {
case ADD_ENTRY:
return Object.assign({}, state, {
entries: state.entries.concat("test2")
});
break;
case DELETE_ENTRY:
break;
}
return state;
}
但是,如果我以以下方式编写化简器,则尽管状态正在更新,但未调用“ render”方法:
export const EntryReducer = (state = initState, action) => {
let newState = Object.assign({}, state);
switch (action.type) {
case ADD_ENTRY:
newState.entries.push("test2");
break;
case DELETE_ENTRY:
break;
}
return newState;
}
我不明白为什么渲染器没有被调用。据我了解,“ newState”是一个不可变的对象,不包含对“ state”对象的任何引用。
请帮助我理解它。谢谢。
答案 0 :(得分:2)
由于Object.assign
很浅,因此不会创建新的数组,而旧的数组将被.push()
突变:
state.entries === newState.entries // ["test1", "test2"]
答案 1 :(得分:2)
“ newState”是一个不变的对象
如果您不自己做,那是普通的对象/数组/等,因此它不是一成不变的。
为什么未调用渲染器
React-redux尽力而为,实际上将您的组件包装到PureComponent
中。这样做是为了使所有connect()
版本的组件不会在调用任何动作时重新呈现,但仅在与相关的商店中更新一次的地方。
要了解相关数据是否已更改,PureComponent
像oldDataProp !== newDataProp
一样进行了比较。突变对象/数组后,检查将失败,因此组件将不会重新呈现。
答案 2 :(得分:1)
您需要使用其他替代方法,因为Object.assign()
复制了属性值。如果源值是对对象的引用,则仅复制该引用值。
您应该使用Deep clone。
let newState= JSON.parse(JSON.stringify(state));
答案 3 :(得分:1)
与更新状态的方式无关。认为它是正确的,尽管我不建议您避免使用常见的最佳做法。
在您的情况下,未调用 render ,因为组件属性未更改...
我相信您可能会有这样的事情:
<Component entries={this.props.entries}/>
和
mapStateToProps = (state) => ({
entries: state.entries
})
如果是这样,那么state.entries
是控制是否重新渲染组件的道具。如果在ADD_ENTRY
操作的状态更改期间它具有相同的值,则不会重新渲染该组件。
所以。回到根源。请记住,在JavaScript state.entries
中是一个 POINTER ,它指向内存中的数组。
当您调用entries.push
时,内存中的数组将被另一个元素扩展-可以。但是我们在state.entries
中拥有的 POINTER值将保持不变。它不会改变。这就是Array.push
的工作方式。
结果<Component entries={this.props.entries}/>
将不会重新呈现。
通过更改newState.entries.push("test2");
newState.entries = newState.entries.concat("test2");
您实际上在更改entries
指针,组件看到属性props.entries
已更改,现在整个东西都重新呈现了...