Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React - setState() on unmounted component

In my react component im trying to implement a simple spinner while an ajax request is in progress - im using state to store the loading status.

For some reason this piece of code below in my React component throws this error

Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the undefined component.

If I get rid of the first setState call the error goes away.

constructor(props) {    super(props);    this.loadSearches = this.loadSearches.bind(this);      this.state = {      loading: false    }  }    loadSearches() {      this.setState({      loading: true,      searches: []    });      console.log('Loading Searches..');      $.ajax({      url: this.props.source + '?projectId=' + this.props.projectId,      dataType: 'json',      crossDomain: true,      success: function(data) {        this.setState({          loading: false        });      }.bind(this),      error: function(xhr, status, err) {        console.error(this.props.url, status, err.toString());        this.setState({          loading: false        });      }.bind(this)    });  }    componentDidMount() {    setInterval(this.loadSearches, this.props.pollInterval);  }    render() {        let searches = this.state.searches || [];          return (<div>            <Table striped bordered condensed hover>            <thead>              <tr>                <th>Name</th>                <th>Submit Date</th>                <th>Dataset &amp; Datatype</th>                <th>Results</th>                <th>Last Downloaded</th>              </tr>            </thead>            {            searches.map(function(search) {                    let createdDate = moment(search.createdDate, 'X').format("YYYY-MM-DD");                  let downloadedDate = moment(search.downloadedDate, 'X').format("YYYY-MM-DD");                  let records = 0;                  let status = search.status ? search.status.toLowerCase() : ''                    return (                  <tbody key={search.id}>                    <tr>                      <td>{search.name}</td>                      <td>{createdDate}</td>                      <td>{search.dataset}</td>                      <td>{records}</td>                      <td>{downloadedDate}</td>                    </tr>                  </tbody>                );            }            </Table >            </div>        );    }

The question is why am I getting this error when the component should already be mounted (as its being called from componentDidMount) I thought it was safe to set state once the component is mounted ?

like image 242
Marty Avatar asked Oct 02 '15 08:10

Marty


People also ask

Can perform a React state update on an unmounted component?

The warning "Can't perform a React state update on an unmounted component" is caused when we try to update the state of an unmounted component. A straight forward way to get rid of the warning is to keep track of whether the component is mounted using an isMounted boolean in our useEffect hook.

How would you solve can't perform a React state update on an unmounted component?

Warning: Can't perform a React state update 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 a useEffect cleanup function.

Can't call setState or forceUpdate on an unmounted component This is a no-op?

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.


1 Answers

Without seeing the render function is a bit tough. Although can already spot something you should do, every time you use an interval you got to clear it on unmount. So:

componentDidMount() {     this.loadInterval = setInterval(this.loadSearches, this.props.pollInterval); }  componentWillUnmount () {     this.loadInterval && clearInterval(this.loadInterval);     this.loadInterval = false; } 

Since those success and error callbacks might still get called after unmount, you can use the interval variable to check if it's mounted.

this.loadInterval && this.setState({     loading: false }); 

Hope this helps, provide the render function if this doesn't do the job.

Cheers

like image 76
Bruno Mota Avatar answered Sep 22 '22 23:09

Bruno Mota