Javascript 反应状态正在意外更改

Javascript 反应状态正在意外更改,javascript,reactjs,state,Javascript,Reactjs,State,我有一个列表(新闻),它来自服务器,每个对象都有多个用户可以编辑的字段。我在名为originalData的状态下保存列表,在名为activeNews的状态下保存第一条新闻: componentDidMount(){ fetch('/api/published/news_published_english').then(res => res.json()).then(news =>{ if(news && news.rows[0]){

我有一个列表(新闻),它来自服务器,每个对象都有多个用户可以编辑的字段。我在名为
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函数中不断更改

这是我在
originalData
上调用
setState
的唯一地方,但它不断更改为传递给
handleChange
的值

this.setState({ originalData: [...news.rows] });

这是react应用程序中的常见错误,您正在创建数组
This.state.news
的副本,该数组是一个对象数组

执行
x[index].value=value
时,实际上是在修改仍在
this.state.news
中引用的原始对象

这-->
constx=[…This.state.news]复制阵列但不复制对象,它们仍在引用原始对象

你需要做的事情是这样的:

const news = this.state.news.map((x, i) => {
  if (i === index) {
    return {...x, value};
  } else return x
})
this.setState({ news })
handleChange(index, value) {
    this.setState(state => (
        { news: state.news.map((item, i) => i === index
            ? { ...item, value } 
            : item
        }
    ));
}

这是react应用程序中的常见错误,您正在创建数组
This.state.news
的副本,该数组是一个对象数组

执行
x[index].value=value
时,实际上是在修改仍在
this.state.news
中引用的原始对象

这-->
constx=[…This.state.news]复制阵列但不复制对象,它们仍在引用原始对象

你需要做的事情是这样的:

const news = this.state.news.map((x, i) => {
  if (i === index) {
    return {...x, value};
  } else return x
})
this.setState({ news })
handleChange(index, value) {
    this.setState(state => (
        { news: state.news.map((item, i) => i === index
            ? { ...item, value } 
            : item
        }
    ));
}

执行此操作时,您正在对
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]
copiedaray[index]
处的对象是同一个对象-
.value
的更改将在这两个位置发生更改。为了解决这个问题,您需要进行深度克隆。您可以通过查找一个库(如lodash)来实现这一点,也可以采用更快的解决方案并按如下方式更改代码:

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 });
}
或同一事物的浓缩版本:

handleChange(index, value) {  
    const arr = [...this.state.news];
    arr[index] = { ...arr[index], value };
    this.setState({ news: arr });
}

执行此操作时,您正在对
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]
copiedaray[index]
处的对象是同一个对象-
.value
的更改将在这两个位置发生更改。为了解决这个问题,您需要进行深度克隆。您可以通过查找一个库(如lodash)来实现这一点,也可以采用更快的解决方案并按如下方式更改代码:

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 });
}
或同一事物的浓缩版本:

handleChange(index, value) {  
    const arr = [...this.state.news];
    arr[index] = { ...arr[index], value };
    this.setState({ news: arr });
}

在代码中,
x[index].value=value
正在修改原始数组,这就是它被破坏的原因。您可以这样编写更改处理程序:

const news = this.state.news.map((x, i) => {
  if (i === index) {
    return {...x, value};
  } else return x
})
this.setState({ news })
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 };
    });
}

在代码中,
x[index].value=value
正在修改原始数组,这就是它被破坏的原因。您可以这样编写更改处理程序:

const news = this.state.news.map((x, i) => {
  if (i === index) {
    return {...x, value};
  } else return x
})
this.setState({ news })
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 };
    });
}

如下面提供的答案所示。。您永远不应该改变当前状态变量。。复制它们,更新值,然后将新值设置为state,如下所示。。您永远不应该改变当前状态变量。。复制它们,更新值,然后将新值设置为stateHi,我经常看到类似这样的代码-->
this.state.news.map((item,I)=>I==index?value:item})
你能解释一下这实际上是如何工作的吗?这是修改当前项中的
属性的缩写吗?@deook:它的作用与您在回答中所做的相同,只是它使用了条件(三元)运算符@caesay,但这不会导致数组包含未修改的对象,然后只包含
,即
[{…},{…},'someValue']
?@deowk是的,在本例中,这是一个bug-它应该是这样的:
this.state.news.map((item,i)=>i==index?{……item,value}:item})大家好,我经常看到类似的代码-->
this.state.news.map((item,I)=>I===index?value:item})
您能解释一下这是如何工作的吗?这是修改当前项中的
属性的缩写吗?@deook:它的作用与您在回答中所做的相同,只是它使用了条件(三元)运算符@caesay,但这不会导致数组包含未修改的对象,然后只包含
,即
[{…},{…},'someValue']
?@deowk是的,在本例中,这是一个bug-它应该是这样的:
this.state.news.map((item,i)=>i==index?{……item,value}:item})