卸载组件时对 setState 做出反应警告

Posted

技术标签:

【中文标题】卸载组件时对 setState 做出反应警告【英文标题】:React warning on setState when unmounting component 【发布时间】:2018-03-20 00:36:58 【问题描述】:

当我执行以下代码时(参见下面的 sn-p),我收到警告:

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

在我的 Mounter 类下的 componentWillUnmount() 方法中我做错了什么吗?谢谢!

class Blinker extends React.Component 
   constructor(props) 
    super();
    this.state = 
      appear: true
    
    this.blinker = this.blinker.bind(this);
  

  blinker()  
    this.setState(appear: !this.state.appear );
  

  componentDidMount() 
    setInterval(this.blinker, 300)
  

  render() 
    return (
      <div>
         (this.state.appear) && "xxx" 
      </div>
    );
  


class Mounter extends React.Component 
  constructor(props) 
    super();
    this.state = 
      render: true
    ;
    this.interval = null;
  

  componentDidMount() 
    this.interval = setTimeout( () =>
      this.rendering(), 1500
    );
  

  componentWillUnmount() 
    clearInterval(this.interval);
  

  rendering() 
    this.setState( render: !this.state.render );
  

  render() 
    return (
      <div>
        <h1>
           this.state.render && <Blinker /> 
        </h1>
      </div>
    );
     




ReactDOM.render(<Mounter />, app);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

<div id="app"></div>

【问题讨论】:

您的 sn-p 不会发出警告。我不确定这是否相关,但您在 setTimeout 函数上调用 clearInterval。你的意思是打电话给clearTimeout 吗? 【参考方案1】:

清除this.interval并在设置状态之前检查它。这将防止在卸载组件时设置状态。

componentWillUnmount() 
  clearInterval(this.interval);
  this.interval = null; // clear


rendering() 
  // this.interval will be null when unmounting so avoid setting state:
  if (this.interval) 
    this.setState(
      render: !this.state.render
    );
  

【讨论】:

【参考方案2】:

计时器方法可能很棘手,因为它们可能在componentWillUnmount 触发之前触发,但状态设置可能在组件卸载后发生,因为setState 是异步的。要解决这个问题,您可以使用react-timer-mixin。

反应定时器混合

使用裸 setTimeout、setInterval、setImmediate 和 requestAnimationFrame 调用是非常危险的,因为如果你忘记 在卸载组件之前取消请求,您将面临风险 回调抛出异常。

如果您包含 TimerMixin,那么您可以将您的调用替换为 setTimeout(fn, 500)this.setTimeout(fn, 500)(只需在前面加上 this.) 一切都会为您妥善清理。

【讨论】:

我认为在这种情况下没有必要向您的应用程序添加依赖项。检查保存的间隔 ID 就足够了,而且代码量更小。 @Scott 感谢您的输入,但这是一种处理计时器的反应建议方式,所以我认为我应该建议这样做。尽管您的解决方案在大多数情况下都很好,但仍然有很小的机会收到警告,因为setState 可以在将来的任何时间完成,如docs 所述。可以在 if 语句之后和 setState 调用之前卸载组件。 javascript 是单线程的,因此无法在 if 语句之后和 setState 调用之前卸载组件。

以上是关于卸载组件时对 setState 做出反应警告的主要内容,如果未能解决你的问题,请参考以下文章

如何切换 css 类以与 setState Hook 做出反应

有没有办法检查反应组件是不是已卸载?

如何解决反应原生 EventEmitterListener 警告

对道具更改做出反应重新渲染

反应路由器卸载功能组件

尝试过滤提取的数据库信息时出现卸载组件警告