web-dev-qa-db-ja.com

componentWillUnmountのclearTimeoutのより良い方法

8秒間読み込まれたときにキャンセルする作業用の読み込みコンポーネントがあります。このコードは動作しますが、私には気分が悪く、これを行うためのより良い方法があるかどうか疑問に思っています。

this.mountedを設定しないと、エラーが発生します。

警告:マウントされたコンポーネントまたはマウントされたコンポーネントのみを更新できます。これは通常、マウントされていないコンポーネントでsetState、replaceState、またはforceUpdateを呼び出したことを意味します。これはノーオペレーションです。 Loadingコンポーネントのコードを確認してください。

これは、タイマーがキャンセルされていないので、this.seStateで続行していると思わせます。 clearTimeoutcomponentWillUnmountを設定するとどうしてですか?グローバルthis.mountedを使用するよりも、これを処理する良い方法はありますか?

class Loading extends Component {
  state = {
    error: false,
  };

  componentDidMount = () => {
    this.mounted = true;
    this.timer();
  };

  componentWillUnmount = () => {
    this.mounted = false;
    clearTimeout(this.timer);
  };

  timer = () =>
    setTimeout(() => {
      (this.mounted && this.setState({ error: true })) || null;
    }, 8000);

  render() {
    const { showHeader = false } = this.props;
    const { error } = this.state;
    return (
      <View style={backgroundStyle}>
        {showHeader && <HeaderShell />}
        {!error &&
          <View style={loadingHeight}>
            <PlatformSpinner size="large" />
          </View>}
        {error && <Error code="service" />}
      </View>
    );
  }
}

Loading.propTypes = {
  showHeader: PropTypes.bool,
};

Loading.defaultProps = {
  showHeader: false,
};

export default Loading;
12
Turnipdabeets

これにより、タイマーがキャンセルされないように思われます

Pointyが言ったように、そうではありません。 functionthis.timer)をclearTimeoutに。 setTimeout戻り値(タイマーのハンドル)を渡す必要があるため、そのハンドルを使用してキャンセルできます。

このような単純なコンポーネントでは、timer関数の必要性はわかりませんが、複雑さが増すだけです。 CDMでタイマーを設定しただけです。

class Loading extends Component {
  state = {
    error: false,
  };

  componentDidMount = () => {                // ***
    // Remember the timer handle             // ***
    this.timerHandle = setTimeout(() => {    // ***
      this.setState({ error: true });        // ***
      this.timerHandle = 0;                  // ***
    }, 8000);                                // ***
  };                                         // ***
                                             // ***
  componentWillUnmount = () => {             // ***
    // Is our timer running?                 // ***
    if (this.timerHandle) {                  // ***
        // Yes, clear it                     // ***
        clearTimeout(this.timerHandle);      // ***
        this.timerHandle = 0;                // ***
    }                                        // ***
  };                                         // ***

  render() {
    const { showHeader = false } = this.props;
    const { error } = this.state;
    return (
      <View style={backgroundStyle}>
        {showHeader && <HeaderShell />}
        {!error &&
          <View style={loadingHeight}>
            <PlatformSpinner size="large" />
          </View>}
        {error && <Error code="service" />}
      </View>
    );
  }
}

Loading.propTypes = {
  showHeader: PropTypes.bool,
};

Loading.defaultProps = {
  showHeader: false,
};

export default Loading;

しかし、示されているよりも多くのロジック、または単に個人的な好みがある場合、はい、別々の機能が良いです:

class Loading extends Component {
  state = {
    error: false,
  };

  componentDidMount = () => {
    this.setTimer();
  };

  componentWillUnmount = () => {
    this.clearTimer();
  };

  setTimer = () => {
    if (this.timerHandle) {
      // Exception?
      return;
    }
    // Remember the timer handle
    this.timerHandle = setTimeout(() => {
      this.setState({ error: true });
      this.timerHandle = 0;
    }, 8000);
  };

  clearTimer = () => {
    // Is our timer running?
    if (this.timerHandle) {
        // Yes, clear it
        clearTimeout(this.timerHandle);
        this.timerHandle = 0;
    }
  };

  render() {
    const { showHeader = false } = this.props;
    const { error } = this.state;
    return (
      <View style={backgroundStyle}>
        {showHeader && <HeaderShell />}
        {!error &&
          <View style={loadingHeight}>
            <PlatformSpinner size="large" />
          </View>}
        {error && <Error code="service" />}
      </View>
    );
  }
}

Loading.propTypes = {
  showHeader: PropTypes.bool,
};

Loading.defaultProps = {
  showHeader: false,
};

export default Loading;
23
T.J. Crowder

setTimeoutから返された値を使用してクリアする必要があります(以下を参照)。ただし、clearTimeoutcomponentWillUnmountを実行するのは正しい方法です。これを実行する人はいません。

  componentDidMount = () => {
    this.mounted = true;
    this.timeout = this.timer();
  };

  componentWillUnmount = () => {
    this.mounted = false;
    clearTimeout(this.timeout);
  };

  timer = () =>
    setTimeout(() => {
      (this.mounted && this.setState({ error: true })) || null;
    }, 8000);
6
Purgatory

React 16.3:この解決策は私にとってはうまくいきましたが、私の場合は別の解決策はうまくいきませんでした:

class Modal extends Component {

  constructor(props) {
    super(props);
    this.timeout = null;
    this.state = {
      clicked: false,
    };
  }

  handleClick = async (e, action) => {
    if (!this.state.clicked) { /
      this.setState( {clicked: true} , async () => {
        const res = await action();
        this.timeout = setTimeout(() => {
        if (this.mounted) this.setState( {clicked: false});
          this.timeout = null;
        }, 2000);

      });
    }
  };

  componentDidMount = () => {
    this.mounted = true;
  };


  componentWillUnmount = () =>{
    this.mounted = false;
    if (this.timeout) {
      clearTimeout(this.timeout)
    }
  };
1
stackdave