Reakcja this.setState nie jest funkcją

288

Jestem nowy w React i próbuję napisać aplikację współpracującą z interfejsem API. Wciąż pojawia się ten błąd:

TypeError: this.setState nie jest funkcją

kiedy próbuję obsłużyć odpowiedź API. Podejrzewam, że coś jest nie tak z tym wiązaniem, ale nie mogę wymyślić, jak to naprawić. Oto kod mojego komponentu:

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
źródło

Odpowiedzi:

348

Oddzwanianie jest wykonywane w innym kontekście. Trzeba bindsię thisw celu uzyskania dostępu do wnętrza zwrotnego:

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

EDYCJA: Wygląda na to, że musisz powiązać oba wywołania initi 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
źródło
@TravisReeder, no. W samouczku nie ma wzmianki o wiązaniu.
Tor Haugen,
8
Prawdopodobnie było to 2,5 roku temu. 😁
Travis Reeder
1
Użyłem funkcji strzałki, aby rozwiązać problem. Dzięki za pomoc
Tarun Nagpal
Czy ktoś może wyjaśnić, co należy rozumieć przez „oddzwanianie odbywa się w innym kontekście”?
SB
135

Możesz uniknąć potrzeby .bind (this) dzięki funkcji strzałki 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
źródło
1
To działa dobrze. W rzeczywistości słowo kluczowe funkcji nie powinno być wyświetlane w pliku es6.
JChen___
6
Twoja odpowiedź pomogła mi :-) Korzystając z klasy ES6 i RN 0.34, znalazłem dwa sposoby na powiązanie „tego” z funkcją wywołania zwrotnego. 1) onChange={(checked) => this.toggleCheckbox()}, 2) onChange={this.toggleCheckbox.bind(this)}.
devdanke
Jest to dobre, o ile nie musisz obsługiwać starych przeglądarek.
Użytkownik
Idealne rozwiązanie
Hitesh Sahu
2
GMsoF, te dwa rozwiązania działają, ponieważ a) gdy to zrobisz .bind(this), ustawia wartość thiskontekstu nadrzędnego, z którego this.toggleCheckbox()jest wywoływany, w przeciwnym thisrazie odnosi się do miejsca, w którym został faktycznie wykonany. b) Rozwiązanie grubej strzały działa, ponieważ zachowuje wartość this, więc pomaga ci, nie gwałtownie zmieniając wartość this. W JavaScript, thispo prostu odnosi się do bieżącego zakresu, więc jeśli napiszesz funkcję, thisjest to funkcja. Jeśli umieścisz w nim funkcję, znajdzie się thisona w tej funkcji potomnej. Grube strzały utrzymują kontekst, skąd zostały wywołane
agm1984,
37

możesz również zapisać odwołanie thisprzed wywołaniem apimetody:

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');
},
użytkownik2954463
źródło
32

React zaleca powiązanie tego we wszystkich metodach, które muszą tego użyć classzamiast tego samego function.

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

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

Lub możesz użyć arrow functionzamiast tego.

uruapanmexicansong
źródło
14

Musisz tylko powiązać swoje wydarzenie

dla byłych

// 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
źródło
10

Teraz ES6 ma funkcję strzałki, więc naprawdę pomocne, jeśli naprawdę mylisz się z wyrażeniem bind (to), możesz wypróbować funkcję strzałki

Tak właśnie robię.

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
źródło
8

Nie musisz przypisywać tego do zmiennej lokalnej, jeśli używasz funkcji strzałki. Funkcje strzałek wiążą się automatycznie i można uniknąć problemów związanych z zasięgiem.

Poniższy kod wyjaśnia, jak używać funkcji strzałek w różnych scenariuszach

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
źródło
5

Teraz w reakcji z es6 / 7 możesz powiązać funkcję z bieżącym kontekstem za pomocą funkcji strzałki w ten sposób, wysyłać żądania i rozwiązywać takie obietnice:

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

Za pomocą tej metody można łatwo wywołać tę funkcję w module componentDidMount i poczekać na dane przed renderowaniem kodu HTML w funkcji renderowania.

Nie znam wielkości twojego projektu, ale osobiście odradzam używanie obecnego stanu komponentu do manipulowania danymi. Powinieneś użyć do tego stanu zewnętrznego, takiego jak Redux lub Flux lub coś innego.

Quentin Malguy
źródło
5

użyj funkcji strzałek, ponieważ funkcje strzałek wskazują zakres nadrzędny, a to będzie dostępne. (substytut techniki wiązania)

Pranav Kapoor
źródło
1
Świetne zwięzłe rozwiązanie
Chun
mam ten sam problem, chociaż wywoływano metodę jako funkcję strzałki w wywołaniu, ale myślę, że muszę zaimplementować funkcję stanu jako taką i dokładnie to, co zrobiłem
Carmine Tambascia
3

Tutaj ten kontekst się zmienia. Użyj funkcji strzałki, aby zachować kontekst klasy 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
źródło
1

Jeśli to robisz i nadal masz problem, moim problemem jest to, że nazwałem dwie zmienne o tej samej nazwie.

Miałem companiesjako przedmiot przywieziony z Firebase, a potem próbowałem zadzwonić this.setState({companies: companies})- z oczywistych powodów nie działało.

LukeVenter
źródło