Реагируйте this.setState не является функцией

289

Я новичок в React и пытаюсь написать приложение, работающее с API. Я продолжаю получать эту ошибку:

Ошибка типа: 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));

РЕДАКТИРОВАТЬ: Похоже, вы должны связать 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, нет. Там нет упоминания о привязке в учебнике.
Тор Хауген
8
Там, наверное, было 2,5 года назад. Tra
Трэвис Ридер
1
Я использовал функцию стрелки, чтобы решить проблему. Спасибо за помощь
Тарун
Может ли кто-нибудь уточнить, что подразумевается под «обратным вызовом в другом контексте»?
SB
135

Вы можете избежать необходимости .bind (this) с помощью функции стрелки 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
источник
1
Это хорошо работает. Фактически, ключевое слово function не должно отображаться в файле es6.
JChen___
6
Ваш ответ помог мне :-) Используя класс ES6 и RN 0.34, я нашел два способа связать «this» с функцией обратного вызова. 1) onChange={(checked) => this.toggleCheckbox()}, 2) onChange={this.toggleCheckbox.bind(this)}.
devdanke
Это хорошо, если вам не нужно поддерживать старые браузеры.
Пользователь
Идеальное решение
Хитеш Саху
2
GMsoF, эти два решения работают, потому что a) когда вы это делаете .bind(this), оно устанавливает значение thisродительского контекста, из которого this.toggleCheckbox()вызывается, иначе thisбудет ссылаться на то, где оно фактически было выполнено. б) Решение с помощью жирной стрелки работает, потому что оно сохраняет значение 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');
},
user2954463
источник
32

React рекомендует связать это во всех методах, которые должны использовать это classвместо этого себя function.

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

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

Или вы можете использовать arrow functionвместо этого.

uruapanmexicansong
источник
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});

}
g8bhawani
источник
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));
 }
Sameel
источник
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})
 }
}

С помощью этого метода вы можете легко вызвать эту функцию в componentDidMount и подождать данных, прежде чем рендерить ваш html в вашей функции рендеринга.

Я не знаю размер вашего проекта, но я лично не рекомендую использовать текущее состояние компонента для манипулирования данными. Вы должны использовать внешнее состояние, такое как Redux или Flux или что-то еще для этого.

Квентин Малгуй
источник
5

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

Пранав Капур
источник
1
Отличное сжатое решение
Chun
Имея ту же проблему, хотя и использовал для вызова метода в качестве функции стрелки в вызове, но я думаю, что я должен реализовать функцию состояния как таковую, и именно то, что я сделал
Кармин 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})- он не работал по понятным причинам.

LukeVenter
источник