为啥在类构造函数中使用 setState 方法时 React 会抛出错误?

Posted

技术标签:

【中文标题】为啥在类构造函数中使用 setState 方法时 React 会抛出错误?【英文标题】:Why does React throw an error when setState method used in a class constructor?为什么在类构造函数中使用 setState 方法时 React 会抛出错误? 【发布时间】:2016-07-29 01:03:22 【问题描述】:

我知道在为尚未安装的组件设置状态时会引发错误。这解释了我使用 setState 函数而不是显式直接设置状态时遇到的错误。

import React, Component from 'react';

class SearchBar extends Component 

constructor(props) 
  super(props);

  this.state = term: ''; // -> seems to be the agreed means to set   initial state
// this.setState(term: ''); // -> generates an error


render() 
  return (
    <div>
      <input onChange=event => this.setState(term:   event.target.value)/>
      Value of the input: this.state.term
  </div>
);
  

当我取消注释第二行 this.setState(term: '') 时出现的错误是:

警告:setState(...):只能更新已安装或正在安装的组件。这通常意味着您在未安装的组件上调用了 setState()。这是一个无操作。请检查组件的代码。

我知道如何防止错误,只需明确设置状态而不告诉 React 任何有关它的信息,我已经看到 github 问题谈论该错误:Github Issue #3878 我想知道的是为什么不能 React 工作出去?如果从构造函数调用 setState 它知道这是第一次使用它?我可能把它简化得太多了,但是如果有人有一个很好的技术答案作为为什么不这样做的原因呢?

【问题讨论】:

setState 触发了一个漫长而复杂的延迟重新渲染过程。作为一个疯狂的猜测 - 他们只是不想使过程复杂化(甚至更进一步),这反过来会带来 0 的好处。 【参考方案1】:

React 类总是使用名为state 的属性进行初始化,该属性设置为null 的值,如source code 所示。如你所知,React 提供了一个setState 方法来操作这个属性。根据docs:

setState() 不会立即改变 this.state 而是创建一个挂起的状态转换。调用此方法后访问 this.state 可能会返回现有值。

不保证 setState 调用的同步操作,并且调用可能会被批处理以提高性能。

setState() 将始终触发重新渲染,除非在 shouldComponentUpdate() 中实现条件渲染逻辑。

简而言之,setState() 是一个异步、多步骤、不可预测的操作,会导致组件重新渲染。调用这样的函数需要一个对象已经完全初始化,因此在类仍在安装时不会发生。它会在类完全初始化之前尝试对其执行生命周期操作。

当然,如果您希望组件以不是null 的状态开始,但又不想立即导致多个渲染和操作发生,那么这会留下一个问题。这就是为什么 React 提供了一种不依赖 setState 来初始化组件状态的方法。在 ES5 中,这是在一个名为 getInitialState 的属性中设置初始状态。但是,ES6 引入了一种原生语法,用于在使用特殊的 constructor 方法初始化类时设置属性(因此 React 不再需要自己的自定义版本)。这就是为什么,如果你想在 React 组件挂载时初始化它的状态,你必须将它声明为this.state = ,而不是使用setState()

【讨论】:

你打败了我,说得好。 @andy-noelker 太棒了,谢谢你的回答非常清楚。我知道为什么用一种方式做某事而不是另一种方式是重要的,我觉得使用图书馆会更舒服,你已经向我解释清楚了。谢谢。 @Faktor10 很高兴我能帮上忙!如果您觉得这回答了问题,您能否继续将其标记为已接受,以便发现此问题的其他人可以看到?

以上是关于为啥在类构造函数中使用 setState 方法时 React 会抛出错误?的主要内容,如果未能解决你的问题,请参考以下文章

为啥无法在类上找到适当的构造函数

在类构造函数中使用 MFC 按钮控件 EnableWindow 方法时“调试断言失败”

c++(在类中)执行buf=new char[i];delete []buf; 为啥没有调用构造和析构函数?

反应:在构造函数上绑定方法时,在 setState 内实现计时器,状态属性未定义

为啥在 CDI 中使用构造函数而不是 setter 注入?

Flutter - 从 websocket 消息动态创建小部件 - 未处理的异常:在构造函数中调用 setState()