8

I'm new to react so I'm sure I'm missing something basic. I'm getting undefined for this, and therefore cannot read property 'setState' attempting to set state within the return of a function that calls fetch, what am I doing wrong? Note I call MyAction from an onClick and the response data is fine.

var ItemComponent = React.createClass({

    getInitialState: function() {
        return {
            the_message: "call that API!"
        };
    },

    doFetch: function() {
        var obj = {
            method: 'POST',
            headers: {
                'Accept': 'application/json',
                'Content-Type': 'application/json'
            }
        };
        return fetch('http://localhost:1337/site/test', obj)
            .then(function(response) {
                return response.json();
            }).then(function(data) {
                return data;
            }).catch((error) => {
                console.error(error);
            });
    },

    MyAction: function() {
        this.doFetch().then(function(response){
            this.setState({
                the_message: response.message
            });
        })
    },

    render: function() {
        return (
            <div>
                <div>{this.props.title}</div><br></br>
                <div>{this.props.price}</div><br></br>
                <div onClick={this.MyAction}>{this.props.qty}</div>
            </div>
        );
    }
});
Massimiliano Kraus
  • 3,638
  • 5
  • 27
  • 47
edencorbin
  • 2,569
  • 5
  • 29
  • 44
  • Possible duplicate of [How does the "this" keyword work?](http://stackoverflow.com/questions/3127429/how-does-the-this-keyword-work) – Andy Ray Feb 12 '17 at 02:35

3 Answers3

11

Use arrow function (() => {}) which keeps the last scope (this) as it is .

MyAction: function(){
    this.doFetch().then((response) => {
        this.setState({
            the_message: response.message
        });
    });
},
Abdennour TOUMI
  • 87,526
  • 38
  • 249
  • 254
8

Your inner promise resolution function won't have this context. One way to fix it:

MyAction: function(){
    this.doFetch().then(function(response){
        this.setState({
            the_message: response.message
        });
    }.bind(this))
},

Read more about that on this StackOverflow question.

Community
  • 1
  • 1
Andy Ray
  • 30,372
  • 14
  • 101
  • 138
  • Pefect, your secondly fixed it, I'm not sure what you are suggesting in your first item, as that's my code unchanged? In any case I'm accepting your answer as soon as it lets me (5 min). Side question, I should really be using ES6 syntax yeah? after getting started on some tutorials, I think this syntax is long toothed. – edencorbin Feb 12 '17 at 02:41
  • Double whoops, I just assumed you were using ES6 classes, the first update was irrelevant. Please close your question as a duplicate. – Andy Ray Feb 12 '17 at 03:47
3

A simple .bind(this) would solve the issue:

render: function() {
return (
<div>
<div>{this.props.title}</div><br></br>
<div>{this.props.price}</div><br></br>
<div onClick={this.MyAction.bind(this)}>{this.props.qty}</div>
</div>
);

By adding .bind(this) you keep the scope within your function.

daniloprates
  • 574
  • 1
  • 5
  • 17
  • 2
    You create a new function on every render doing it like this. It's better to bind the handler in the constructor or create an arrow function inside the class. – Isaac Pak Aug 31 '18 at 14:23