状态得到更新,而无需显式设置
我偶然遇到了这个,这让我感到困惑。据我了解,您可以按照以下步骤进行操作:
1-创建当前状态的副本 2-修改副本 3-将修改后的副本分配给状态,从而对其进行更新。
这就是我的方法,而且效果很好。
handleChange(index, event) {
const {name, value} = event.target;
this.setState((prevState) => {
const newState = [...prevState.items];
newState[index][name]= value;
return{items: newState};
});
但是奇怪的是,下面的代码也可以正常工作,但是不必像上面那样在最后一行中显式地分配它。只要我更新了不是它的引用的副本,它就会更新。
handleChange(index, event) {
const {name, value} = event.target;
this.setState((prevState) => {
const newState = [...prevState.items];
newState[index][name]= value;
return{}; //it works as long as I have the brackets.
});
答案 0 :(得分:1)
从您问题中的代码来看,您的州似乎具有类似于以下内容的结构:
state = {项目:[{name1:'index0Value1',name2:'index0Value2'},{name1:'index1Value1',name2:'index1Value2'}]};
其中items
是对象数组。
当您这样做:
const newState = [...prevState.items];
您仅在复制数组,但是newState
数组中的对象仍与原始状态共享。因此,您可以在不影响原始对象的情况下将新对象推送到数组中,但是在这样做时:
newState[index][name]= value;
您正在以原始状态和新状态更改该对象。运行下面的代码片段以查看演示。
const prevState = { items: [{name1: 'index0Value1', name2: 'index0Value2'}, {name1: 'index1Value1', name2: 'index1Value2'}] };
document.getElementById('origbefore').innerHTML = 'prevState before='+JSON.stringify(prevState);
const newItems = [...prevState.items];
newItems[0]['name1']='tryingToOnlyChangeNewState';
const newState = { items: newItems };
document.getElementById('origafter').innerHTML = 'prevState after='+JSON.stringify(prevState);
document.getElementById('new').innerHTML = 'newState='+JSON.stringify(newState);
<div id="origbefore"></div>
<div id="origafter"></div>
<div id="new"></div>
因此,在两个示例中,您的分配都在更改prevState,并且由于setState
支持传递与先前状态合并的部分状态,因此当您返回空对象时,React会将其与已变异的先前状态合并状态,然后重新渲染结果。
答案 1 :(得分:1)
您正在使用spread syntax复制状态。传播语法仅执行浅表复制。因此,当您更改新对象的嵌套属性时,它也会同时更改原始对象的嵌套属性。这就是第二个代码起作用的原因。您的变异状态与当前状态合并,即当前状态,您会看到变异的结果。不要改变您的状态。
大多数时候,您应该使用类似map
,filter
之类的方法来突变新复制的对象,并且仅更改vale而不会改变原始状态。因此,您可以将这些方法与扩展语法或Object.assign混合使用,但不要直接更改复制的方法。
由于我们不知道您的数据的确切形状,因此我只是在模仿以下代码。如果您有索引,则可以使用此技术。
class App extends React.Component {
state = {
items: [{ foo: "a foo" }, { bar: "a bar" }, { baz: "a baz" }],
};
handleChange(index, event) {
const { name, value } = event.target;
const { items } = this.state;
const newItems = Object.assign([], items, {
[index]: { ...items[index], [name]: value },
});
this.setState({ items: newItems });
}
render() {
return (
<div>
{this.state.items.map((item, index) => (
<input
name={Object.keys(item)}
key={Object.keys(item)}
index={index}
onChange={(event) => this.handleChange(index, event)}
/>
))}
<div>
{this.state.items.map((item) => (
<p key={Object.values(item)}>{Object.values(item)}</p>
))}
</div>
</div>
);
}
}
ReactDOM.render(
<App />,
document.getElementById("root")
);
<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>
<div id="root"></div>
但是,如果您像在第二个示例中那样更改此代码,则会发现它不起作用,因为我们不在此处更改原始状态。
class App extends React.Component {
state = {
items: [{ foo: "a foo" }, { bar: "a bar" }, { baz: "a baz" }],
};
handleChange(index, event) {
const { name, value } = event.target;
const { items } = this.state;
const newItems = Object.assign([], items, {
[index]: { ...items[index], [name]: value },
});
// this.setState({ items: newItems });
this.setState({});
}
render() {
return (
<div>
{this.state.items.map((item, index) => (
<input
name={Object.keys(item)}
key={Object.keys(item)}
index={index}
onChange={(event) => this.handleChange(index, event)}
/>
))}
<div>
{this.state.items.map((item) => (
<p key={Object.values(item)}>{Object.values(item)}</p>
))}
</div>
</div>
);
}
}
ReactDOM.render(
<App />,
document.getElementById("root")
);
<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>
<div id="root"></div>