[英]Difference between …state, { } and Object.assign({ }, state, { })
[英]Avoid state mutation by Object.assign() inside map()
在 componentDidMount 我這樣做了:
apps.forEach(app => {
if (chosenAppId) {
if (app.id === chosenAppId) {
this.setState({ map: this.props.map });
}
}
});
現在,當我在某些功能中執行此操作時:
this.setState({
...this.state.map,
areas: this.state.map.areas.map(el =>
el._id === area._id
? Object.assign(el, {
chooseDevice: false,
editModal: true
})
: Object.assign(el, { chooseDevice: false })
)
});
我有持久的 redux 狀態,在這種情況下,我希望在重新加載 this.state.map === this.props.map 但不知何故這個 object.assign 改變了我的 redux 狀態,並且在重新加載時所有都被保存到 reducer。
我縮小了范圍,它與 object.assign 有關系,因為如果我將 .concat() 與 this.state.map 相關聯,則不會更改 redux 狀態。
如何? 我真的不明白。 沒有調度 redux action,不知道這是怎么發生的。
行Object.assign(el, { chooseDevice: false })
將改變el
。
看起來你從 props(因此很可能是 Redux 商店)復制到 state 中。 因此,它與 Redux 存儲中已經存在的對象引用相同,因此您正在改變存儲中的值。
請注意,我們的官方 Redux Toolkit 包默認包含一個突變檢測中間件,當您不小心改變值時會拋出錯誤。
您對問題的根本原因是完全正確的 - Object.assign()
正在改變您在map()
中引用的原始數組項。
要解決這個問題,只需擺脫Object.assign()
改變您的狀態:
this.setState({
...this.state.map,
areas: this.state.map.areas.map(el => ({
...el,
chooseDevice: false,
...(el._id === area.id && {editModal: true})
}))
});
當您傳播一個對象時,它不會克隆現有屬性,它只是將它們復制到新對象中,這意味着實例屬性將保留,例如
const obj = { numbers: [1, 2, 3], person: { name: 'Foo' } } const copy = { ...obj }; copy.numbers.push(4); copy.person.name = 'Bar' console.log(obj.numbers) // [1,2,3,4] console.log(obj.person) // { name: 'Bar' } console.log(copy.numbers) // [1,2,3,4] console.log(copy.person) // { name: 'Bar' }
注意原始對象是如何通過對副本的更改而更新的
因此,當您將狀態擴展到本地狀態時,即
...this.state.map
然后在實例屬性上使用Object.assign
,您在不經意間同時更新了 Redux 狀態。
Object.assign()
方法將所有可枚舉的自身屬性從一個或多個源對象復制到目標對象。
Object.assign(target, ...sources)
目標
目標對象 - 應用源屬性的對象,修改后返回。
來源
源對象 — 包含您要應用的屬性的對象。
Object.assign() - JavaScript | MDN
如果您不希望更改提供的元素,請定位一個空對象。 這樣el
和第三個參數中的附加數據將被分配給一個新對象,而不是覆蓋el
屬性。
this.setState(prevState => ({
...prevState.map,
areas: prevState.map.areas.map(el =>
el._id === area._id
? Object.assign({}, el, {
chooseDevice: false,
editModal: true
})
: Object.assign({}, el, { chooseDevice: false })
)
}));
如果你想讓 ir 更緊湊,你也可以把它變成一個對象傳播,並使用內聯 if 來改變editModal
的條件。
this.setState(prevState => ({
...prevState.map,
areas: prevState.map.areas.map(el => ({
...el,
chooseDevice: false,
editModal: el._id === area._id ? true : el.editModal
}))
}));
編輯: this.state
不應在setState
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.