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

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

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

    });

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

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.


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

}

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

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

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.


5

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


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

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.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.