如何清除在组件生命周期中创建的setTimeout?

时间:2019-03-21 22:58:12

标签: javascript reactjs ecmascript-6

问题是我收到此警告:

  

警告:无法在已卸载的组件上调用setState(或forceUpdate)。这是空操作,但它表明应用程序中发生内存泄漏。要修复此问题,请取消componentWillUnmount方法中的所有订阅和异步任务。

原因是这样的:

  componentDidUpdate(props, state) {
    const {
      pushNotificationData,
      unassignedPickUpPassengers,
      unassignedDropOffPassengers,
    } = this.props;

    if (props.pushNotificationData !== pushNotificationData) {
      this.applyOpacityForSomeSeconds(0.5, 0);
    }

    if (
      props.unassignedPickUpPassengers !== unassignedPickUpPassengers ||
      props.unassignedDropOffPassengers !== unassignedDropOffPassengers
    ) {
      this.applyOpacityForSomeSeconds(1, 1000);
    }
  }

  applyOpacityForSomeSeconds = (setOpacity, timeout) =>
    setTimeout(() => {
      this.setState({ lastIndexOpacity: setOpacity });
    }, timeout);

如何清除componentWillUnmount的超时?是这样做的正确方法还是其他方法?

3 个答案:

答案 0 :(得分:2)

您需要保留它的引用,例如:

applyOpacityForSomeSeconds = (setOpacity, timeout) =>
  clearTimeout(this.timeout) // notice that you don't have to check if this.timeout is null or undefined just let clearTimeout handle it

  this.timeout = setTimeout(() => {
    this.setState({ lastIndexOpacity: setOpacity });
  }, timeout);

注意,该引用设置为this.timeout = setTimeout(...。您可以在构造函数中或其他地方使用null初始化引用-我总是在犹豫。 :)

如果需要,可以将clearTimeout(this.timeout)移动或复制到componentWillUnmount中。

答案 1 :(得分:1)

一个简单的解决方案:

componentWillUnmount() {
  this.isUnmounted = true;
}

然后您可以将其添加到超时回调中:

setTimeout(() => {
  if (!this.isUnmounted) {
    this.setState({ lastIndexOpacity: setOpacity });
  }
}, timeout);

如果卸载了组件,这将阻止setState运行。

答案 2 :(得分:0)

setTimeout返回可以与cancelTimeout一起使用的句柄。您也可以将该句柄放到您的状态:

applyOpacityForSomeSeconds = (setOpacity, timeout) => {
    this.setState({
        opacityTimeout: setTimeout(() => {
            this.setState({
                lastIndexOpacity: setOpacity,
                opacityTimeout: null
            });
        }, timeout);
    });
}

componentWillUnmount() {
    if (this.state.opacityTimeout) {
        clearTimeout(this.state.opacityTimeout);
    }
}

由于您有1秒钟的超时,因此可能有可能同时运行多个超时,具体取决于您的应用程序为此组件更新道具的速度。在这种情况下,最好使用句柄数组:

(假设您的初始状态会将opacityTimeouts初始化为一个空数组)

applyOpacityForSomeSeconds = (setOpacity, timeout) => {
    const handle = setTimeout(() => {
        this.setState({
            lastIndexOpacity: setOpacity,
            opacityTimeouts: this.state.opacityTimeouts.filter(
                h => h !== handle
            )
        });
    }, timeout);

    this.setState({
        opacityTimeouts: [
           ...this.state.opacityTimeouts,
           handle
        ]
    });
}

componentWillUnmount() {
    this.state.opacityTimeouts.forEach(handle => clearTimeout(handle));
}