Réagissez this.setState n'est pas une fonction

289

Je suis nouveau dans React et j'essaie d'écrire une application fonctionnant avec une API. Je reçois toujours cette erreur:

TypeError: this.setState n'est pas une fonction

lorsque j'essaie de gérer la réponse de l'API. Je soupçonne que quelque chose ne va pas avec cette liaison, mais je ne sais pas comment y remédier. Voici le code de mon composant:

var AppMain = React.createClass({
    getInitialState: function() {
        return{
            FirstName: " "
        };
    },
    componentDidMount:function(){
        VK.init(function(){
            console.info("API initialisation successful");
            VK.api('users.get',{fields: 'photo_50'},function(data){
                if(data.response){
                    this.setState({ //the error happens here
                        FirstName: data.response[0].first_name
                    });
                    console.info(this.state.FirstName);
                }

            });
        }, function(){
        console.info("API initialisation failed");

        }, '5.34');
    },
    render:function(){
        return (
            <div className="appMain">
            <Header  />
            </div>
        );
    }
});
Ivan
la source

Réponses:

348

Le rappel est effectué dans un contexte différent. Vous devez le faire bindpour thisavoir accès à l'intérieur du rappel:

VK.api('users.get',{fields: 'photo_50'},function(data){
    if(data.response){
        this.setState({ //the error happens here
            FirstName: data.response[0].first_name
        });
        console.info(this.state.FirstName);
    }

}.bind(this));

EDIT: On dirait que vous devez lier à la fois les appels initet api:

VK.init(function(){
        console.info("API initialisation successful");
        VK.api('users.get',{fields: 'photo_50'},function(data){
            if(data.response){
                this.setState({ //the error happens here
                    FirstName: data.response[0].first_name
                });
                console.info(this.state.FirstName);
            }

        }.bind(this));
    }.bind(this), function(){
    console.info("API initialisation failed");

    }, '5.34');
Davin Tryon
la source
@TravisReeder, non. Il n'y a aucune mention de bind dans le tutoriel.
Tor Haugen
8
Il y a probablement 2,5 ans. 😁
Travis Reeder
1
J'ai utilisé la fonction flèche pour résoudre le problème. Merci pour l'aide
Tarun Nagpal
Quelqu'un peut-il élaborer davantage sur ce que l'on entend par «rappel est effectué dans un contexte différent»?
SB
135

Vous pouvez éviter d'avoir besoin de .bind (ceci) avec une fonction de flèche ES6.

VK.api('users.get',{fields: 'photo_50'},(data) => {
        if(data.response){
            this.setState({ //the error happens here
                FirstName: data.response[0].first_name
            });
            console.info(this.state.FirstName);
        }

    });
goodhyun
la source
1
Cela fonctionne bien. En fait, le mot-clé de fonction ne doit pas apparaître dans un fichier es6.
JChen___
6
Votre réponse m'a aidé :-) En utilisant une classe ES6 et RN 0.34, j'ai trouvé deux façons de lier "ceci" à une fonction de rappel. 1) onChange={(checked) => this.toggleCheckbox()}, 2) onChange={this.toggleCheckbox.bind(this)}.
devdanke
C'est bien tant que vous n'avez pas besoin de prendre en charge les anciens navigateurs.
Utilisateur
Solution parfaite
Hitesh Sahu
2
GMsoF, ces deux solutions fonctionnent car a) lorsque vous le faites .bind(this), il définit la valeur de thisdans le contexte parent d'où il this.toggleCheckbox()est appelé, sinon il thisferait référence à l'endroit où il a été réellement exécuté. b) La solution Fat Arrow fonctionne car elle conserve la valeur de this, donc elle vous aide en ne modifiant pas violemment la valeur de this. En JavaScript, thisse réfère simplement à la portée actuelle, donc si vous écrivez une fonction, thisc'est cette fonction. Si vous y mettez une fonction, thisc'est à l'intérieur de cette fonction enfant. Les flèches grasses gardent le contexte d'où appelé
agm1984
37

vous pouvez également enregistrer une référence à thisavant d'appeler la apiméthode:

componentDidMount:function(){

    var that = this;

    VK.init(function(){
        console.info("API initialisation successful");
        VK.api('users.get',{fields: 'photo_50'},function(data){
            if(data.response){
                that.setState({ //the error happens here
                    FirstName: data.response[0].first_name
                });
                console.info(that.state.FirstName);
            }
        });
    }, function(){
        console.info("API initialisation failed");

    }, '5.34');
},
user2954463
la source
32

React recommande de lier ceci dans toutes les méthodes qui doivent utiliser ceci classplutôt que ceci function.

constructor(props) {
    super(props)
    this.onClick = this.onClick.bind(this)
}

 onClick () {
     this.setState({...})
 }

Ou vous pouvez utiliser à la arrow functionplace.

uruapanmexicansong
la source
14

Vous avez juste besoin de lier votre événement

par exemple

// place this code to your constructor

this._handleDelete = this._handleDelete.bind(this);

// and your setState function will work perfectly

_handleDelete(id){

    this.state.list.splice(id, 1);

    this.setState({ list: this.state.list });

    // this.setState({list: list});

}
g8bhawani
la source
10

Maintenant, ES6 a la fonction flèche, il est vraiment utile si vous confondez vraiment avec l'expression de liaison (cette), vous pouvez essayer la fonction flèche

Voilà comment je fais.

componentWillMount() {
        ListApi.getList()
            .then(JsonList => this.setState({ List: JsonList }));
    }

 //Above method equalent to this...
     componentWillMount() {
         ListApi.getList()
             .then(function (JsonList) {
                 this.setState({ List: JsonList });
             }.bind(this));
 }
Sameel
la source
8

Vous n'avez pas besoin de l'affecter à une variable locale si vous utilisez la fonction flèche. Les fonctions fléchées prennent automatiquement la liaison et vous pouvez éviter les problèmes liés à la portée.

Le code ci-dessous explique comment utiliser la fonction flèche dans différents scénarios

componentDidMount = () => {

    VK.init(() => {
        console.info("API initialisation successful");
        VK.api('users.get',{fields: 'photo_50'},(data) => {
            if(data.response){
                that.setState({ //this available here and you can do setState
                    FirstName: data.response[0].first_name
                });
                console.info(that.state.FirstName);
            }
        });
    }, () => {
        console.info("API initialisation failed");

    }, '5.34');
 },
Hemadri Dasari
la source
5

Maintenant, en réaction avec es6 / 7, vous pouvez lier la fonction au contexte actuel avec une fonction de flèche comme celle-ci, faire une demande et résoudre des promesses comme ceci:

listMovies = async () => {
 const request = await VK.api('users.get',{fields: 'photo_50'});
 const data = await request.json()
 if (data) {
  this.setState({movies: data})
 }
}

Avec cette méthode, vous pouvez facilement appeler cette fonction dans le componentDidMount et attendre les données avant de rendre votre html dans votre fonction de rendu.

Je ne connais pas la taille de votre projet mais je déconseille personnellement d'utiliser l'état actuel du composant pour manipuler des données. Vous devez utiliser un état externe comme Redux ou Flux ou autre chose pour cela.

Quentin Malguy
la source
5

utilisez les fonctions fléchées, car les fonctions fléchées pointent vers la portée parent et cela sera disponible. (substitut de la technique de liaison)

Pranav Kapoor
la source
1
Une excellente solution succincte
Chun
ayant le même problème, mais utilisé pour appeler la méthode en tant que fonction de flèche dans l'appel, mais je suppose que je dois implémenter la fonction d'état en tant que telle, et exactement ce que j'ai fait
Carmine Tambascia
3

Ici, ce contexte change. Utilisez la fonction flèche pour garder le contexte de la classe React.

        VK.init(() => {
            console.info("API initialisation successful");
            VK.api('users.get',{fields: 'photo_50'},(data) => {
                if(data.response){
                    this.setState({ //the error happens here
                        FirstName: data.response[0].first_name
                    });
                    console.info(this.state.FirstName);
                }

            });
        }, function(){
        console.info("API initialisation failed");

        }, '5.34');
Shubham Gupta
la source
1

Si vous faites cela et que vous rencontrez toujours un problème, mon problème est que j'appelais deux variables du même nom.

J'avais companiescomme objet ramené de Firebase, puis j'essayais d'appeler this.setState({companies: companies})- cela ne fonctionnait pas pour des raisons évidentes.

LukeVenter
la source