更新状态 - 为啥在调用 setState 时创建新的状态副本?

Posted

技术标签:

【中文标题】更新状态 - 为啥在调用 setState 时创建新的状态副本?【英文标题】:Updating state - why creating a new copy of state when calling setState?更新状态 - 为什么在调用 setState 时创建新的状态副本? 【发布时间】:2020-03-13 01:09:42 【问题描述】:

反应文档:

永远不要直接改变this.state,因为之后调用setState() 可能会替换您所做的突变。像对待this.state 一样对待 不可变。

这很清楚。

class App extends React.Component 
  state = 
   data: []
   

以下我理解

  updateState(event) 
   const name, value = event.target;
   let user = this.state.user; // this is a reference, not a copy...
   user[name] = value; // 
   return this.setState(user); // so this could replace the previous mutation
  

下面这个我看不懂

  updateState(event) 
  const name, value = event.target;
  let user = ...this.state.user, [name]: value;
  this.setState(user);
  

我明白(如前面的例子),我不应该只是:

    不调用setState直接改变状态;或 对其进行变异,然后使用 setState。

但是,为什么我不能(没有直接突变)调用 setState 而不创建一个新的状态副本(没有传播运算符/Object.assign)?以下会有什么问题:

  getData = () => 
   axios.get("example.com") ...
    this.setState(
     data:response.data
    )
   

为什么会这样:

  getData = () => 
   axios.get("example.com") ...
    this.setState(
     data:[...data, response.data]
    )
   

 render () 
  ...
   

【问题讨论】:

因为在您的示例中您想将响应数据添加到数组的末尾,而不是用它替换数组?它们根本不等价,这与突变问题是分开的。 您这样做是因为您只需要处理新的数据实例。这些新实例是根据前一个实例结合新数据创建的。 【参考方案1】:

以下会有什么问题:

this.setState(
   data: response.data,
);

绝对没有,除非您不想将this.state.data 的内容替换为response.data

为什么会这样:

this.setState(
   data: [...data, response.data],
);

因为通过传播,您不会丢失 this.state.data 的内容 - 您基本上是在将新响应推送到 data 数组中。

注意:您应该在setState 中使用回调来从this.state 访问当前的data

this.setState((prevState) => (
   data: [...prevState.data, response.data],
));

【讨论】:

啊,好的,我现在明白了。创建状态的副本仅适用于我想要添加而不是替换某些内容到数组或对象的情况。谢谢!! @javascripting:如果它与 React 中的状态相关,请记住避免变异函数。你真的不想使用例如pushsplice 直接在状态上操作时。您必须使用其他一些不会发生变异并返回新实例的函数,例如sliceconcat 或传播语法。 @javascripting 或者基本上先创建一个深层副本,然后您就可以在该副本上使用变异函数,而不会有直接改变状态的风险。 另外,我还有一个问题。使用 spad operator/Object.assign 或 concat 等仅在其不是更深的嵌套状态时才有效。例如: var state = _2ndLevel: arr: [] --> 在这种情况下,嵌套对象仍然会被引用,所以我最终会直接改变状态。在这里做什么有好处(不使用任何不变性库助手)?谢谢!! @javascripting 在深度状态的情况下,你只需要更深入;) 解构整个状态,直到你到达它的指定部分。无论如何,是的,最好保持状态尽可能平坦。

以上是关于更新状态 - 为啥在调用 setState 时创建新的状态副本?的主要内容,如果未能解决你的问题,请参考以下文章

调用 setState 时,我的有状态小部件不会更新其状态

ComponentDidMount 中调用的 setState 没有更新状态? [复制]

Flutter:状态类:为啥不在 setState 方法调用之前改变状态变量,而不是在 setState 方法中

为啥即使在有状态小部件中使用 setstate 也无法获取更新的变量。因为我想在新的 TabBar 选项上更新我的 Container

为啥 setState 回调会抛出错误:“来自 useState() 和 useReducer() Hooks 的状态更新不支持第二个回调参数...”

在循环中调用 setState 只会更新状态 1 次