3

在下面的示例中,我使用 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

4 回答 4

2

来自文档:setState

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

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

于 2020-06-19T14:28:26.213 回答
1

你错了:

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

您期望状态应该用空对象更新。但事实并非如此。

当您使用 setState 方法时,它期望属性更新。但是由于您没有向 setState 提供任何属性,因此它什么也不做。但它仍会按 setState 的性质重新渲染您的组件。

更新:对您的查询

编辑:我还想指出(因为许多答案是指我传递一个空对象的事实)如果我像这样调用 setState 组件将被重新渲染(和更新):

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

这似乎不应该导致重新渲染

React 内部声明它类似于this.state.results您尝试使用完全相同的状态更新状态。所以,没有什么可以重新渲染的。但是当你对空对象使用 setState 时,React 会刷新它的状态,所以会发生重新渲染。

文档中阅读此注释:

笔记

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

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

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

于 2020-06-19T14:27:23.813 回答
1

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

在任何时候,React 库都会维护两个 Virtual DOM 副本

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

虚拟 DOM 然后通过称为协调的过程与真实 DOM 同步。

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

参考-

于 2020-06-20T16:16:36.927 回答
0

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

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

所以在上面的代码片段中,每次调用都会this.setState({});创建一个新的状态对象,这会使组件重新渲染。

于 2020-06-19T14:31:33.410 回答