为什么在直接更改状态时React会更新?

时间:2020-06-19 14:13:27

标签: javascript reactjs setstate

在下面的示例中,我使用ES6 Map作为React中的状态值:

class App extends React.Component {
  constructor(props) {
    super(props);
    const results = new Map();
    results["group1"] = [{ value: "..." }, { value: "..." }];
    this.state = { results };
  }

  onUpdateClick(i) {
    this.state.results["group1"][i].value = i;
    this.setState({});
  }

  onResetClick(i) {
    this.state.results["group1"][i].value = "...";
    this.setState({});
  }

  render() {
    const { results } = this.state;
    return (
      <div>
        {results["group1"].map((r, i) => (
          <div>
            {r.value}&nbsp;
            <button onClick={e => this.onUpdateClick(i)}>update</button>
            <button onClick={e => this.onResetClick(i)}>reset</button>
          </div>
        ))}
      </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById("container"));
<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='container'></div>

当您单击按钮时,我直接更新了Map,就位,然后不带任何参数调用setState。我不制作地图的克隆/深拷贝。根据我对React文档的理解,这应该行不通,并且在文档中明确警告:

切勿直接更改this.state,因为此后调用setState()可能会替换您所做的更改。将this.state视为不可变

https://reactjs.org/docs/react-component.html#state

文档还指出比较比较浅,因此使用空对象进行调用肯定会导致没有合并,因此没有重新渲染?

此示例为何起作用?

(还要注意,我也在React v16.9.0中重现了此行为)

编辑: 我还想指出(由于许多答案都指向我传递一个空对象这一事实),如果我这样调用setState,则该组件将被重新渲染(并更新):

this.setState({ results: this.state.results })

似乎不应该引起重新渲染

4 个答案:

答案 0 :(得分:2)

摘自文档:setState

setState()将始终导致重新渲染,除非shouldComponentUpdate()返回false。

这意味着this.setState({});导致重新渲染,无论您是否将更改作为参数传递

答案 1 :(得分:1)

您错了:

this.setState({}); // this will do nothing.

您期望状态应该用空对象更新。但这不是。

使用setState方法时,它期望属性更新。但是,由于您没有为setState提供任何属性,因此它将什么都不做。但是它仍然会通过setState性质重新呈现您的组件。

更新:针对您的查询

编辑:我也想指出(因为很多答案都涉及到我传递一个空对象这一事实),如果我这样调用setState,则该组件将被重新渲染(并更新):

this.setState({ results: this.state.results })

似乎不应该引起重新渲染

内部反应表明,当您尝试使用完全相同的状态更新状态时,它类似于this.state.results。因此,没有什么要重新渲染的。但是,当将setState与空对象一起使用时,React会闪烁其状态,以便进行重新渲染。

docs阅读此注释:

注意

避免将道具复制到状态中!这是一个常见的错误:

constructor(props) {
 super(props);
 // Don't do this!
 this.state = { color: props.color };
}

问题在于,这既不必要(您可以直接使用this.props.color),又会产生错误(对颜色道具的更新不会反映在状态中)。

答案 2 :(得分:1)

根据文档, setState()将始终导致重新渲染,除非shouldComponentUpdate()返回false。 您可以将 render()函数视为创建React元素树。在下一个状态或道具更新时,该render()函数将返回不同的React元素树(虚拟DOM )。

在任何时候,反应库维护两个虚拟DOM副本

触发对 setState()的请求时,React将创建一个新树,其中包含组件中的反应性元素(以及和更新后的状态)。 通过与前一棵树的元素进行比较,该树用于确定组件的UI应如何响应状态变化而更改。

虚拟DOM,然后通过称为对帐的过程与实际DOM同步。

对于突变,不进行直接突变可以避免项目中不必要的错误,因为 setState()是异步的,这意味着您不能期望setState立即更新状态(它批量执行),因此以前的任何突变都可以被先前的setState状态更新操作覆盖。

参考-

答案 3 :(得分:0)

当调用this.setState时,基本上React会将我们传递给该函数的参数与旧的this.state合并以产生新的状态值。例如:

this.state = Object.assign({}, this.state, passedParam);

因此,在以上代码段中,每次调用this.setState({});时,都会创建一个新的状态对象,该对象将重新呈现Component。