Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can't call setState (or forceUpdate) on an unmounted component. React

Gutentag, guys!

I keep getting this error message from my application after unmounting the component:

Warning: Can't call setState (or forceUpdate) on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.
    in Header (at index.js:27)

Now here is the code from the Header component:

class Header extends Component {
  isCancelled = false;
  state = {
    someStateVars: x,
    separateColumns: 'true',
  }

  handleChange = (event) => {
    const target = event.target;
    const value = target.type === 'checkbox' ? target.checked : target.value;
    const name = target.name;

    if (!this.isCancelled) {
      this.setState({ //######THIS IS LINE 27######
        [name]: value
      });
    }
  }

  handleDisplayChange = (event) => {
    const value = event.target.value;
    const name = 'separateColumns';

    if (!this.isCancelled) {
      this.setState({  
        [name]: value
      }, () => {
        this.props.displayChange();
      });
    }
  }

  serversRefresh = () => {

    if (!this.isCancelled) {
      setTimeout(() => {
        this.setState({refreshed: false});
      }, localStorage.getItem('seconds')*1000); //disable refresh for 15 seconds
    }
  }

  reactivateButton = () => {
    if (!this.isCancelled) this.setState({refreshed: false});
  }

  componentDidMount() {
    if(localStorage.getItem('seconds')>5 && !this.isCancelled){
      this.setState({refreshed: true});
    }
  }

  componentWillUnmount() {
    this.isCancelled = true;
  }
}

When I saw I was getting this error, I've added isCancelled variable which is changed in componentWillUnmount() function to true.

After I unmount the Header component, after 15 seconds, when the serversRefreshbutton is reactivated, I get this error message.

How can I fix it?

On another component where I've encountered this issue "isCancelled" var did help, but here I see it has no impact and the issue persists.

like image 598
zilijonas Avatar asked Sep 08 '18 13:09

zilijonas


1 Answers

Just store your timeout in a variable, e.g.

this.timeout = setTimeout(/* your actions here*/, /* your timeout */)

and then clear your timeout in the componentWillUnmount

componentWillUnmount() {
    clearTimeout(this.timeout)
}

It should fix your problem without crutches like this.isCancelled. Detecting component's mount state is no-op, because it still unloaded from memory even after unmounting.

setTimeout returns timer's id with which it can be cancelled in future. clearTimeout cancels timeout by it's id, if it is not yet executed.

More about your case you can read here: Why isMounted is antipattern.

More about timers on MDN.

like image 66
Limbo Avatar answered Nov 14 '22 23:11

Limbo