Eo Riendeau
Eo Riendeau

Reputation: 3

ReactJS: Uncaught (in promise) this.setState is not a function

I am dealing with the following frustrating error:

Home.js:231 Uncaught (in promise) TypeError: _this9.setState is not a function. The error is coming from the last line of the following function:

checkIfRunning() {
  return fetch('/api/following/iscurrentlyrunning', {
    credentials: 'include',
  })
    .then(response => {
      console.log(response.status);
      if (response.status === 200) {
        return response.json();
      }
    })
    .then(response => {
      let cState = this.state;
      cState.running = response;
      this.setState(cState);
    });
}

I did bind the function in the component constructor and when I call it alone, it works fine. The issue arise when I try to invoke the function in a timer (setInterval). In componentWillMount, I call few functions:

componentWillMount() {
  this.checkIfFirstTimeLogin()
    .then(() => {
      // user already exists
      if (!this.state.firstLogin) {
        this.Name();
        this.getRole();
        setInterval(() => this.checkIfRunning(), 10000);
      }
    })
    .then(() => {
      let cState = this.state;
      cState.pageLoading = false;
      this.setState(cState);
    })
    .catch(error => console.log(error));
}

I have the intuition that the promise chain breaks the binding for a reason I do not presently understand.

Thank you for any help,

Upvotes: 0

Views: 2731

Answers (4)

Ashot Pahlevanyan
Ashot Pahlevanyan

Reputation: 91

You are mutating the state directly, it is not allowed, in the final example you are still doing it. It is better to use an Object.assign(…) to create new object like this :

let newState = Object.assign({}, ...this.state,  running: response);

Then, only do your setState() call

this.setState(newState);

One of the fundamental principles of React is that changes to State are not done directly but with the setState function which will put the change to queue, and it will be done either alone or with batch update.

Upvotes: 0

Eo Riendeau
Eo Riendeau

Reputation: 3

Thanks all for the help, very appreciated.

I solved the problem with the following fix, although I am not sure why it works now:

checkIfRunning() {
  return fetch('/api/following/iscurrentlyrunning', {
  credentials: 'include',
 })
.then(response => {
  console.log(response.status);
  if (response.status === 200) {
    return response.json();
  }
})
.then(response => {
  let cState = this.state;
  cState.running = response;
  this.setState({cState});
});

}

Notice how this.setState(cState) became this.setState({cState}).

Thanks all for your time, it led to interesting research on my part.

Upvotes: 0

Thanh Ngo
Thanh Ngo

Reputation: 365

You can try change function checkIfRunning() {} to checkIfRunning = () => {} to pass this into function

Upvotes: 0

Danny Delott
Danny Delott

Reputation: 7008

Promises are a guaranteed future, which means the whole promise chain will fire once invoked and there's little you can do to stop it.

On a practical level, this means you need to check to be sure that your component instance is still mounted before trying to access setState off it, as the component may have unmounted before this promise chain completes.

.then(response => {
    ...code here...
    // important! check that the instance is still mounted!
    if (this.setState) {
      this.setState(cState);
    }
});

Also, you should never mutate local state directly as you are doing here:

// don't mutate state directly, use setState!
let cState = this.state;
cState.running = response;

Upvotes: 1

Related Questions