React this.setState не є функцією


288

Я новачок у React і намагаюся написати додаток, що працює з API. Я продовжую отримувати цю помилку:

TypeError: this.setState не є функцією

коли я намагаюся обробити відповідь API. Я підозрюю, що з цим прив'язкою щось не так, але я не можу зрозуміти, як це виправити. Ось код мого компонента:

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

Відповіді:


348

Зворотний виклик проводиться в іншому контексті. Вам потрібно зробити bindце this, щоб мати доступ усередині зворотного дзвінка:

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: Схоже, вам потрібно зв’язати initі 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, ні. У підручнику не згадується про зв'язування.
Tor Haugen

8
Там, напевно, було 2,5 роки тому. 😁
Тревіс Редер

1
Я використовував функцію стрілки для вирішення проблеми. Дякую за допомогу
Тарун Нагпал

Чи може хтось детальніше розглянути, що означає "зворотний виклик робиться в іншому контексті"?
СБ

135

Ви можете уникнути необхідності .bind (це) за допомогою функції стрілки 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
Це добре працює. Насправді ключове слово функції не повинно відображатися у файлі es6.
JChen___

6
Ваша відповідь допомогла мені :-) Використовуючи клас ES6 та RN 0,34, я знайшов два способи прив’язати "це" до функції зворотного виклику. 1) onChange={(checked) => this.toggleCheckbox()}, 2) onChange={this.toggleCheckbox.bind(this)}.
devdanke

Це добре, доки вам не потрібно підтримувати старі браузери.
Користувач

Ідеальне рішення
Hitesh Sahu

2
GMsoF, ці два рішення працюють тому, що: а) коли ви це робите .bind(this), він встановлює значення thisбатьківського контексту, звідки this.toggleCheckbox()викликається, інакше thisбуде посилатися на те, де воно було реально виконано. b) Розчин жирної стрілки працює, оскільки він зберігає значення this, тому допомагає вам, не змінюючи насильницького значення this. У JavaScript thisпросто посилається на поточну область, тому якщо ви пишете функцію, thisце ця функція. Якщо ви помістите функцію всередину, thisце всередині цієї дочірньої функції. Жирові стрілки зберігають контекст, куди дзвонили
agm1984

37

Ви також можете зберегти посилання, thisперш ніж викликати apiметод:

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 рекомендує пов'язувати це у всіх методах, для яких потрібно використовувати це classзамість цього self function.

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

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

Або ви можете використовувати arrow functionзамість цього.


14

Вам просто потрібно зв’язати свою подію

для екс-

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

Тепер у ES6 функція стрілок - це дуже корисно, якщо ви дійсно плутаєте з виразом bind (this), ви можете спробувати функцію стрілки

Так я і роблю.

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

Вам не потрібно призначати цю локальну змінну, якщо ви використовуєте функцію стрілки. Функції стрілок автоматично прив’язуються, і ви можете залишатися осторонь проблем, пов’язаних із сферою застосування.

Нижче коду пояснюється, як використовувати функцію стрілки в різних сценаріях

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

Тепер у взаємодії з es6 / 7 ви можете прив’язати функцію до поточного контексту за допомогою такої функції стрілки, зробити запит і вирішити такі обіцянки:

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

За допомогою цього методу ви можете легко викликати цю функцію в компонентіDidMount і чекати даних, перш ніж надати html у функції візуалізації.

Я не знаю розміру вашого проекту, але я особисто раджу не використовувати поточний стан компонента для маніпулювання даними. Для цього слід використовувати зовнішній стан, такий як Redux або Flux, або щось інше.


5

використовуйте функції стрілок, оскільки функції стрілок вказують на батьківський обсяг, і це буде доступно. (заміна техніки зв’язування)


1
Чудове лаконічне рішення
Чун

маючи ту саму проблему, хоч і використовується для виклику методу як функції стрілки під час виклику, але я думаю, що я повинен реалізувати функцію стану як таку, і саме те, що я зробив
Carmine Tambascia

3

Тут ЦЕЙ контекст змінюється. Використовуйте стрілку для збереження контексту класу 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

Якщо ви це робите і у вас все ще виникають проблеми, моя проблема полягає в тому, що я називав дві змінні однойменною назвою.

Я був companiesоб'єктом, завезеним із Firebase, а потім намагався зателефонувати this.setState({companies: companies})- він не працював із зрозумілих причин.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.