Better way to clearTimeout in componentWillUnmount

Viewed 25006

I have a working Loading Component that cancels out when it has been loading for 8 seconds. This code works but it feels off to me and I'm wondering if there is a better way to do this.

Without setting this.mounted I get the error:

Warning: Can only update a mounted or mounting component. This usually means you called setState, replaceState, or forceUpdate on an unmounted component. This is a no-op. Please check the code for the Loading component.

This make me think that the timer is not getting canceled so it continues with this.seState. Why would that be if I set clearTimeout in componentWillUnmount? Is there a better way to handle this than using a global 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;
3 Answers

React 16.3: this solution worked for me, the anothers solutios did not work in my case:

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)
    }
  };
Related