[英]React state variable updates automatically without calling setState
我面临以下问题,无法弄清楚。
我在 state 中有两个变量,称为 userDetails 和 userDetailsCopy。 在 componentDidMount 中,我正在调用 API 并将数据保存在 userDetails 和 userDetailsCopy 中。
出于比较目的,我正在维护另一个名为 userDetailsCopy 的副本。
我只更新 setState 内的 userDetails,但即使 userDetailsCopy 也得到更新,而不是有旧的 API 数据。
下面是代码:
constructor(){
super()
this.state={
userDetails:{},
userDetailsCopy: {}
}
}
componentDidMount(){
// API will return the following data
apiUserDetails : [
{
'name':'Tom',
'age' : '28'
},
{
'name':'Jerry',
'age' : '20'
}
]
resp.data is nothing but apiUserDetails
/////
apiCall()
.then((reps) => {
this.setState({
userDetails: resp.data,
userDetailsCopy: resp.data
})
})
}
updateValue = (text,i) => {
let userDetail = this.state.userDetails
userDetail[i].name = text
this.setState({
userDetails: userDetail
})
}
submit = () => {
console.log(this.state.userDetials) // returns updated values
console.log(this.state.userDetailsCopy) // also return updated values instead of returning old API data
}
Need a quick solution on this.
这样做的问题是您认为您正在通过执行此操作在 state 中复制 object
let userDetail = this.state.userDetails
userDetail.name = text
但是,在 Javascript 中,对象不是这样复制的,它们是通过引用传递的。 因此,此时的 userDetail 包含对userDetails
中的 userDetails 的引用,当您改变userDetail
时,它会改变 state 中的 userDetail。
参考: https://we-are.bookmyshow.com/understanding-deep-and-shallow-copy-in-javascript-13438bad941c
要将 object 从 state 正确克隆到本地变量,您需要改为执行以下操作:
let userDetail = {...this.state.userDetails}
OR
let userDetail = Object.assign({}, this.state.userDetails)
永远记住,对象是通过引用而不是值传递的。
编辑:我没有正确阅读问题,但上述答案仍然有效。 userDetailCopy
也被更新的原因是因为resp.data
是通过引用传递给它们的,并且编辑它们中的任何一个都会编辑另一个。
React state 和它的数据应该被视为不可变的。
从反应文档:
永远不要直接改变
this.state
,因为之后调用setState()
可能会替换你所做的突变。 将this.state
视为不可变。
以下是如何将 state 视为不可变的五种方法:
方法 #1:Object.assign 和 Array.concat
updateValue = (text, index) => {
const { userDetails } = this.state;
const userDetail = Object.assign({}, userDetails[index]);
userDetail.name = text;
const newUserDetails = []
.concat(userDetails.slice(0, index))
.concat(userDetail)
.concat(userDetails.slice(index + 1));
this.setState({
userDetails: newUserDetails
});
}
方法 2:Object 和阵列扩展
updateValue = (text, index) => {
const { userDetails } = this.state;
const userDetail = { ...userDetails[index], name: text };
this.setState({
userDetails: [
...userDetails.slice(0, index),
userDetail,
...userDetails.slice(index + 1)
]
});
}
方法#3:不变性助手
import update from 'immutability-helper';
updateValue = (text, index) => {
const userDetails = update(this.state.userDetails, {
[index]: {
$merge: {
name: text
}
}
});
this.setState({ userDetails });
};
方法 #4:Immutable.js
import { Map, List } from 'immutable';
updateValue = (text, index) => {
const userDetails = this.state.userDetails.setIn([index, 'name'], text);
this.setState({ userDetails });
};
方法#5:沉浸式
import produce from "immer";
updateValue = (text, index) => {
this.setState(
produce(draft => {
draft.userDetails[index].name = text;
})
);
};
注意:选项#1 和#2 只进行浅层克隆。 因此,如果您的 object 包含嵌套对象,则这些嵌套对象将按引用而不是按值复制。 因此,如果您更改嵌套的 object,您将改变原始的 object。
要保持userDetailsCopy
不变,您需要保持state
(当然还有state.userDetails
)的不变性。
function getUserDerails() { return new Promise(resolve => setTimeout( () => resolve([ { id: 1, name: 'Tom', age: 40 }, { id: 2, name: 'Jerry', age: 35 } ]), 300 )); } class App extends React.Component { state = { userDetails: [], userDetailsCopy: [] }; componentDidMount() { getUserDerails().then(users => this.setState({ userDetails: users, userDetailsCopy: users })); } createChangeHandler = userDetailId => ({ target: { value } }) => { const { userDetails } = this.state; const index = userDetails.findIndex(({ id }) => id === userDetailId); const userDetail = {...userDetails[index], name: value }; this.setState({ userDetails: [...userDetails.slice(0, index), userDetail, ...userDetails.slice(index + 1) ] }); }; render() { const { userDetails, userDetailsCopy } = this.state; return ( <React.Fragment> {userDetails.map(userDetail => ( <input key={userDetail.id} onChange={this.createChangeHandler(userDetail.id)} value={userDetail.name} /> ))} <pre>userDetails: {JSON.stringify(userDetails)}</pre> <pre>userDetailsCopy: {JSON.stringify(userDetailsCopy)}</pre> </React.Fragment> ); } } ReactDOM.render( <App />, document.getElementById("root") );
<div id="root"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.