我正面临以下问题,无法解决。
在状态下,我有两个变量称为userDetails和userDetailsCopy。在componentDidMount中,我要进行API调用,并将数据保存在userDetails和userDetailsCopy中。
出于比较目的,我正在维护另一个名为userDetailsCopy的副本。
我只更新setState内的userDetails,但即使userDetailsCopy也正在更新,而不是具有旧的API数据。
下面是代码:
'value: ' + String(v)
答案 0 :(得分:2)
问题在于您认为您正在通过这样做来复制状态下的对象
let userDetail = this.state.userDetails
userDetail.name = text
但是,在Javascript中,对象不是像这样复制的,而是通过引用传递的。因此,此时的userDetail包含在您的状态下对userDetails
的引用,并且当您对userDetail
进行突变时,它就会对状态中的那个进行突变。
ref:https://we-are.bookmyshow.com/understanding-deep-and-shallow-copy-in-javascript-13438bad941c
要正确地将对象从状态克隆到本地变量,您需要执行以下操作:
let userDetail = {...this.state.userDetails}
OR
let userDetail = Object.assign({}, this.state.userDetails)
永远记住,对象是通过引用传递的,而不是值。
编辑:我没有正确阅读问题,但以上答案仍然有效。 userDetailCopy
之所以也要更新,是因为resp.data
是通过引用传递给它们两个的,并且编辑其中任何一个都会编辑另一个。
答案 1 :(得分:1)
反应状态及其数据应视为不可变的。
请勿直接更改
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:即时
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>