2

I get an error when trying to change the state of a component.

Uncaught TypeError: Cannot read property 'setState' of undefined

constructor(props){
    super(props);

    this.state={
        r:'',
        message:''
    };
    this.setStateMessage = this.setStateMessage.bind(this);
}
setStateMessage (e){
    e.preventDefault();
    var test = this.state.message;

    request
      .post('http://127.0.0.1:5000/api/db')
      .send({message: this.state.message})
      .accept('application/json')
      .withCredentials()
      .end(function(err, res){
        if(err)
            throw err;
        this.setState({ r: res.body.message });
      });
}

render() {
    return (
        <div>
            <div className='response'>
                {this.state.r}
            </div>
            //form with input
        </div>
    )}
Ando
  • 1,802
  • 4
  • 25
  • 47

3 Answers3

6

This is because you are calling this.setState from within a function so this is actually a reference to the function you are in. You need to either store a reference to the correct this or use an arrow which does not have its own context and inherits from the parent context. So:

setStateMessage (e){
  e.preventDefault();
  var test = this.state.message;
  var self = this;

  request
    .post('http://127.0.0.1:5000/api/db')
    .send({message: this.state.message})
    .accept('application/json')
    .withCredentials()
    .end(function(err, res){
      if(err) throw err;
      self.setState({ r: res.body.message });
  });
}

Or:

setStateMessage (e){
  e.preventDefault();
  var test = this.state.message;

  request
    .post('http://127.0.0.1:5000/api/db')
    .send({message: this.state.message})
    .accept('application/json')
    .withCredentials()
    .end((err, res) => {
      if(err) throw err;
      this.setState({ r: res.body.message });
  });
}
aray12
  • 1,833
  • 1
  • 16
  • 22
  • I woke up today and had the idea that `this` actually relates to the callback function so that is the culprit of the problem. My best guess was to write something like `var res = request.post('......);` and then maybe use the `res` in a setState outside the callback function? Would that be a third option? – Ando May 08 '16 at 14:44
  • And one final question... If I was using Redux i could of used `dispatch` and that would of worked right? Is that because Redux's functions are globally defind? – Ando May 08 '16 at 14:45
  • `dispatch` is not defined globally. If you are using redux you should be calling the API from within an async action using `redux-thunk`. It is a little hard to answer your question with an example. But because you wouldn't be calling `this.setState` from within this callback, then you wouldn't have this problem. That said you should do some reading on `this` in JS. Otherwise you are going to get confused somewhere else. – aray12 May 09 '16 at 01:24
2

To add to @aray12's answer, you can bind the callback too.

setStateMessage (e){
  e.preventDefault();
  var test = this.state.message;

  request
    .post('http://127.0.0.1:5000/api/db')
    .send({message: this.state.message})
    .accept('application/json')
    .withCredentials()
    .end((function(err, res) {
      if(err) throw err;
      this.setState({ r: res.body.message });
  }).bind(this));
}
xyc
  • 151
  • 1
  • 6
0

After spending too much time on this my final code is here

class Data extends React.Component{

     constructor(){
        super()
        this.state={
            name:''
        }
    }

    componentDidMount(){
        console.log('componentDidMount');

        var url = "http:\//localhost:3000/order/test";
        Request
        .get(url)
        .query(null)
        .set('Accept', 'application/json')
        .end ((error, response)=>{
            const title=response.text
            console.log(JSON.stringify(title));

            this.setState({
              name:title
            });
        });

    }

    render(){
        return <div>World {this.state.name}</div>;
    }

}

Note: If response is a text you have to use response.text as I use in my case

If you want more detail click here

Ain
  • 708
  • 1
  • 8
  • 16