我现在面临着以下问题,想不通。
我在状态里面有两个变量,叫做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.
这个问题是你认为你这样做是在做状态中对象的复制
let userDetail = this.state.userDetails
userDetail.name = text
但是,在Javascript中,对象不是这样复制的,而是通过引用传递的。所以userDetail在这一点上包含了对的引用。userDetails
在你的国家,当你突变了 userDetail
它去变异州的那个。
参考文献。https:/we-are.bookmyshow.comunderstanding-deep and shallow-copy-in-javascript-13438bad941c。
为了正确地将对象从状态克隆到你的本地变量,你需要这样做。
let userDetail = {...this.state.userDetails}
OR
let userDetail = Object.assign({}, this.state.userDetails)
永远记住,对象是通过引用而不是值来传递的。
EDIT:我没看清楚问题,但上面的答案还是有效的。原因是 userDetailCopy
被更新也是因为 resp.data
是通过对它们两个的引用传递的,编辑其中任何一个都会编辑另一个。
React状态和它的数据应该被视为不可改变的。
从 React文档:
永不变异
this.state
直呼setState()
之后可能会取代你所做的突变。治療this.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:对象和数组展开
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:Immer
import produce from "immer";
updateValue = (text, index) => {
this.setState(
produce(draft => {
draft.userDetails[index].name = text;
})
);
};
注意:选项#1和#2只做浅层克隆。因此,如果你的对象包含嵌套对象,那些嵌套对象将通过引用而不是通过值来复制。所以,如果你改变了嵌套对象,你会突变原始对象。
为了保持 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>