Reaccionar this.setState no es una función

288

Soy nuevo en React y estoy tratando de escribir una aplicación que funcione con una API. Sigo recibiendo este error:

TypeError: this.setState no es una función

cuando trato de manejar la respuesta API. Sospecho que hay algún problema con este enlace, pero no puedo encontrar la manera de solucionarlo. Aquí está el código de mi 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>
        );
    }
});
Ivan
fuente

Respuestas:

348

La devolución de llamada se realiza en un contexto diferente. Debe hacerlo bindpara thistener acceso dentro de la devolución de llamada:

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

EDITAR: Parece que tienes que vincular las llamadas inity 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
fuente
@TravisReeder, no. No hay mención de enlace en el tutorial.
Tor Haugen
8
Probablemente hubo hace 2.5 años. 😁
Travis Reeder
1
Usé la función de flecha para resolver el problema. Gracias por la ayuda
Tarun Nagpal
¿Puede alguien dar más detalles sobre lo que se entiende por "devolución de llamada se realiza en un contexto diferente"?
SB
135

Puede evitar la necesidad de .bind (esto) con una función de flecha 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
fuente
1
Esto funciona bien De hecho, la palabra clave de función no debe mostrarse en un archivo de es6.
JChen___
66
Su respuesta me ayudó :-) Usando una clase ES6 y RN 0.34, encontré dos formas de vincular "esto" a una función de devolución de llamada. 1) onChange={(checked) => this.toggleCheckbox()}, 2) onChange={this.toggleCheckbox.bind(this)}.
devdanke
Esto es bueno siempre que no necesite admitir navegadores antiguos.
Usuario
Solución perfecta
Hitesh Sahu
2
GMsoF, esas dos soluciones funcionan porque a) cuando lo hace .bind(this), establece el valor del thiscontexto primario desde donde this.toggleCheckbox()se llama, de lo contrario thisse referiría a dónde se ejecutó realmente. b) La solución de flecha gorda funciona porque retiene el valor de this, por lo que te ayuda al no cambiar violentamente el valor de this. En JavaScript, thissimplemente se refiere al alcance actual, por lo que si escribe una función, thises esa función. Si coloca una función dentro de ella, thisestá dentro de esa función secundaria. Las flechas
gruesas
37

También puede guardar una referencia thisantes de invocar el apimétodo:

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

React recomienda vincular esto en todos los métodos que necesitan usar esto en classlugar de esto de sí mismo function.

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

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

O puede usar arrow functionen su lugar.

uruapanmexicansong
fuente
14

Solo necesitas vincular tu evento

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

}
g8bhawani
fuente
10

Ahora ES6 tiene la función de flecha, es realmente útil si realmente confundes con la expresión bind (this) puedes probar la función de flecha

Así es como lo hago.

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
fuente
8

No es necesario asignar esto a una variable local si utiliza la función de flecha. Las funciones de flecha se vinculan automáticamente y puede mantenerse alejado de los problemas relacionados con el alcance.

El siguiente código explica cómo usar la función de flecha en diferentes escenarios

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
fuente
5

Ahora, en reaccionar con es6 / 7, puede vincular la función al contexto actual con la función de flecha como esta, hacer una solicitud y resolver promesas como esta:

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

Con este método, puede llamar fácilmente a esta función en el componenteDidMount y esperar los datos antes de representar su html en su función de representación.

No sé el tamaño de su proyecto, pero personalmente aconsejo no usar el estado actual del componente para manipular datos. Debe usar un estado externo como Redux o Flux o algo más para eso.

Quentin Malguy
fuente
5

use las funciones de flecha, ya que las funciones de flecha apuntan al alcance primario y esto estará disponible. (sustituto de la técnica de enlace)

Pranav Kapoor
fuente
1
Una gran solución sucinta
Chun
aunque tengo el mismo problema, solía llamar al método como una función de flecha en la llamada, pero supongo que tengo que implementar la función de estado como tal, y exactamente lo que he hecho
Carmine Tambascia
3

Aquí ESTE contexto está cambiando. Use la función de flecha para mantener el contexto de la clase 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
fuente
1

Si está haciendo esto y todavía tiene un problema, mi problema es que estaba llamando a dos variables con el mismo nombre.

Tenía companiesun objeto traído de Firebase, y luego estaba tratando de llamar this.setState({companies: companies}), no estaba funcionando por razones obvias.

LukeVenter
fuente