我有一个列表(新闻),它来自服务器,每个对象都有多个字段,用户可以编辑。我将列表保存在名为originalData
的状态中,并将第一个新闻项保存在名为activeNews
的状态中:
componentDidMount(){
fetch('/api/published/news_published_english').then(res => res.json()).then(news =>{
if(news && news.rows[0]){
const new_news = [...news.rows];
this.setState({originalData:[...news.rows]});
this.setState({activeNews:new_news[0].doc.content});
}
});
}
当用户单击另一个新闻项时,activeNews
将更改为所选索引。 handleChange
获取用户要在活动新闻项中更改的字段的索引及其值:
handleChange(index, value) {
const x = [...this.state.news];
x[index].value = value;
this.setState({ news: x });
console.log("original", this.state.originalData);
console.log("news", news);
}
现在的问题是为什么originalData
不断更新用户更改的值?我希望originalData
保持不变,但在handleChange函数中它会不断变化!
这是我在setState
上调用originalData
的唯一地方,但它一直更改为传递给handleChange
的值。
this.setState({ originalData: [...news.rows] });
答案 0 :(得分:1)
这是React应用程序中的一个常见错误,您正在创建this.state.news
数组的副本,该数组是对象数组。
当您执行x[index].value = value
时,实际上是在修改this.state.news
中仍在引用的原始对象。
此-> const x = [...this.state.news];
复制数组,但不复制对象,它们仍在引用原始对象。
您需要做的是这样的:
const news = this.state.news.map((x, i) => {
if (i === index) {
return {...x, value};
} else return x
})
this.setState({ news })
答案 1 :(得分:1)
执行此操作时,您正在handleChange
函数中对原始对象进行突变:
x[index].value = value;
为了更好地显示正在发生的事情,我将扩展您的代码
handleChange(index, value) {
const copiedArray = [...this.state.news];
const originalObject = copiedArray[index];
originalObject.value = value;
this.setState({ news: copiedArray });
}
正如我在这里使用变量名所逃避的,即使您创建了一个新数组-this.state.news[index]
和copiedArray[index]
的对象是同一对象-因此更改{{1} }在这两个地方都会改变。为了解决这个问题,您需要进行深度克隆。您可以通过找到可以做到这一点的库(例如lodash)来做到这一点,或者可以采用更快的解决方案并按如下所示更改代码:
.value
或同一件事的精简版:
handleChange(index, value) {
const copiedArray = [...this.state.news];
const originalObject = copiedArray[index];
const copiedObject = Object.assign({}, originalObject);
copiedObject.value = value;
copiedArray[index] = copiedObject;
this.setState({ news: copiedArray });
}
答案 2 :(得分:1)
在您的代码中,x[index].value = value
正在修改原始数组,这就是它被破坏的原因。您可以这样编写更改处理程序:
handleChange(index, value) {
this.setState(state => (
{ news: state.news.map((item, i) => i === index
? { ...item, value }
: item
}
));
}
请注意,我在setState中使用了回调函数,当您的新状态依赖于先前状态时,建议使用此功能。
编辑实际上,由于您已经知道索引,因此不需要映射整个数组。看一下caesay的答案,这在性能上会更好:
handleChange(index, value) {
this.setState(state => {
const newsCopy = [ ...state.news ];
newsCopy [index] = { ...arr[index], value };
return { news: newsCopy };
});
}