【问题标题】:React warning on setState when unmounting component卸载组件时对 setState 做出反应警告
【发布时间】: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 吗?

标签: javascript reactjs ecmascript-6 components


【解决方案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 调用之前卸载组件。
    猜你喜欢
    • 2018-10-06
    • 2019-03-20
    • 2017-12-25
    • 1970-01-01
    • 2019-09-05
    • 2017-01-16
    • 2016-12-23
    • 2018-03-25
    • 2019-07-09
    相关资源
    最近更新 更多