Reagisci this.setState non è una funzione


289

Sono nuovo in React e sto provando a scrivere un'app che funziona con un'API. Continuo a ricevere questo errore:

TypeError: this.setState non è una funzione

quando provo a gestire la risposta API. Ho il sospetto che ci sia qualcosa di sbagliato in questa associazione ma non riesco a capire come risolverlo. Ecco il codice del mio componente:

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

Risposte:


348

La richiamata viene effettuata in un contesto diverso. È necessario bindper thisal fine di avere accesso all'interno della richiamata:

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: sembra che devi associare sia il initche il apicall:

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, no. Nel tutorial non si fa menzione del legame.
Tor Haugen,

8
Probabilmente ci sono stati 2,5 anni fa. 😁
Travis Reeder

1
Ho usato la funzione freccia per risolvere il problema. Grazie per l'aiuto
Tarun Nagpal,

Qualcuno può approfondire ulteriormente cosa si intende per "callback viene effettuato in un contesto diverso"?
SB

135

È possibile evitare la necessità di .bind (questo) con una funzione freccia 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
Funziona bene In effetti, la parola chiave della funzione non deve essere visualizzata in un file di es6.
JChen___

6
La tua risposta mi ha aiutato :-) Usando una classe ES6 e RN 0.34, ho trovato due modi per associare "questo" a una funzione di callback. 1) onChange={(checked) => this.toggleCheckbox()}, 2) onChange={this.toggleCheckbox.bind(this)}.
Devdanke,

Questo va bene fino a quando non è necessario supportare i vecchi browser.
Utente

Soluzione perfetta
Hitesh Sahu,

2
GMsoF, queste due soluzioni funzionano perché a) quando lo fai .bind(this), imposta il valore del thiscontesto principale da cui this.toggleCheckbox()viene chiamato, altrimenti thisfarebbe riferimento a dove è stato effettivamente eseguito. b) La soluzione della freccia grassa funziona perché mantiene il valore di this, quindi ti aiuta non cambiando violentemente il valore di this. In JavaScript, thissi riferisce semplicemente all'ambito corrente, quindi se si scrive una funzione, thisè quella funzione. Se inserisci una funzione al suo interno, thisè all'interno di quella funzione figlio. Le frecce grasse mantengono il contesto da dove vengono chiamate
agm1984,

37

potresti anche salvare un riferimento thisprima di invocare il apimetodo:

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 raccomanda di associarlo a tutti i metodi che devono usare questo classinvece di questo di sé function.

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

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

Oppure puoi usare arrow functioninvece.


14

Devi solo associare il tuo evento

per ex-

// 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

Ora ES6 ha la funzione freccia, è molto utile se confondi davvero con l'espressione bind (questa) puoi provare la funzione freccia

Questo è come lo faccio.

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

Non è necessario assegnarlo a una variabile locale se si utilizza la funzione freccia. Le funzioni freccia prendono automaticamente l'associazione e puoi stare lontano dai problemi relativi all'ambito.

Il codice seguente spiega come utilizzare la funzione freccia in diversi scenari

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

Ora, in reazione a es6 / 7, puoi associare la funzione al contesto attuale con la funzione freccia come questa, fare richiesta e risolvere promesse come questa:

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

Con questo metodo puoi facilmente chiamare questa funzione in componentDidMount e attendere i dati prima di renderizzare il tuo html nella tua funzione di rendering.

Non conosco le dimensioni del tuo progetto, ma sconsiglio personalmente di utilizzare lo stato corrente del componente per manipolare i dati. Dovresti usare uno stato esterno come Redux o Flux o qualcos'altro per quello.


5

utilizzare le funzioni freccia, poiché le funzioni freccia indicano l'ambito padre e questo sarà disponibile. (sostituto della tecnica di associazione)


1
Un'ottima soluzione succinta
Chun

avendo lo stesso problema, anche se usato per chiamare il metodo come una funzione freccia nella chiamata, ma immagino che devo implementare la funzione di stato in quanto tale, ed esattamente quello che ho fatto
Carmine Tambascia,

3

Qui QUESTO contesto viene modificato. Utilizzare la funzione freccia per mantenere il contesto della 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

Se lo stai facendo e hai ancora un problema, il mio problema è che stavo chiamando due variabili con lo stesso nome.

Avevo companiesun oggetto portato da Firebase e poi stavo provando a chiamare this.setState({companies: companies})- non funzionava per ovvi motivi.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.