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>
        );
    }
});

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');

@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);
        }

    });

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');
},

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.


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});

}

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));
 }

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');
 },

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.


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)


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');

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.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.