EugenSunic
EugenSunic

Reputation: 13703

this.setState makes variable undefined?

I'm trying to increment my state variable, but whenever I do so an error pops up which says that the variable state is undefined. I defined my variable inside the constructor. Obviously something is going on in the setState function.

Here is the code which renders an error:

displayDiv(){
  var arr=[];
  if (this.state.flag[2]){
    this.setState({counter:counter+4}); // error:undefined
    for (let i = this.state.counter-4; i < this.state.counter; i++)
      arr.push(this.state.arrayHeader[0].content);
    }
  }
 return(
   arr.map(function(item,i){
     return (
       <div className="bodydiv col-sm-3">
         <Header/>
         <Content/>
         <Tags/>
       </div>
     );

    })
  );
}

Here is the defined variable inside the constructor:

constructor(props){
  super(props);

  this.state = {
    arrayHeader:[],
    arraytag1:[],
    arraytag2:[],
    counter:0,
    flag:[false,false,false]
  }
}

Upvotes: 4

Views: 7116

Answers (4)

Carsten
Carsten

Reputation: 2147

To add to the other good answers:

In short, you probably want something like this to be safe:

this.setState((prevState) => ({
    ...prevState, counter: prevState.counter+4
}));

Yes, your current problem was due to the undefined local variable counter in this statement:

this.setState({ counter: counter+4 });

A simple solution (as pointed out by other answers already) is to refer to the respective state variable:

this.setState({ counter: this.state.counter+4});

As pointed out by cdaiga, you'd probably want to preserve the other properties on your state since setState() is replacing the current state with the parameter provided to it and you'd effectively loose all other values on it. You could do it like this:

this.setState({ ...this.state, counter: this.state.counter+4 });

As per the React Documentation, you should also take the asynchronous nature of setState() into account.

React may batch multiple setState() calls into a single update for performance.

Because this.props and this.state may be updated asynchronously, you should not rely on their values for calculating the next state.

For example, this code may fail to update the counter:

// Wrong
this.setState({
    counter: this.state.counter + this.props.increment,
});

To fix it, use a second form of setState() that accepts a function rather than an object. That function will receive the previous state as the first argument, and the props at the time the update is applied as the second argument:

// Correct
this.setState((prevState, props) => ({
    counter: prevState.counter + props.increment
}));

We used an arrow function above, but it also works with regular functions


In your specific example, we would end up with something like this (as already stated at the top):

this.setState((prevState) => ({
    ...prevState, counter: prevState.counter+4
}));

Additional side note: due to this asynchronous nature of this.setState(), accessing this.state after calling this.setState() will still return its old value. Your original posted code is accessing this.state.counter immediately afterwards:

this.setState({counter:counter+4}); // error:undefined
for (let i = this.state.counter-4; i < this.state.counter; i++)
    arr.push(this.state.arrayHeader[0].content);
}

Fortunately in this case you're not using the variable i inside the loop and the this.state.arrayheader[0].content is not being updated, i.e. the following would be equivalent:

this.setState((prevState) => ({
    ...prevState, counter: prevState.counter+4
}));
for (let i = 0; i < 4; i++) {
     arr.push(this.state.arrayHeader[0].content);
}

Upvotes: 3

Robsonsjre
Robsonsjre

Reputation: 1606

I think its simple, try this in function displayDiv

this.setState({counter: this.state.counter+4});

you forgot the this.state at this line :)

Upvotes: 3

cdaiga
cdaiga

Reputation: 4937

You can replace:

this.setState({counter:counter+4}); // error:undefined

with:

const { counter } = this.state; // getting the counter from state
this.setState({counter:counter+4});

But then hope you really want to flush out all the other attributes of the state. Else you should do:

const { counter } = this.state;
this.setState(Object.assign({},this.state,{counter: counter + 4})); 

See how to use Object.assign() method

Upvotes: 1

zurfyx
zurfyx

Reputation: 32797

Your counter +4 points nowhere.

It should be this.state.counter + 4

Also, if you are calling this function on the render() make sure it has been previously binded, you can do that like this on the constructor:

this.displayDiv = this.displayDiv.bind(this);

Upvotes: 9

Related Questions