React this.setState не является функцией

Я новичок в 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>
        );
    }
});

Ответ 1

Обратный вызов выполняется в другом контексте. Для доступа к обратному вызову необходимо 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');

Ответ 2

Вы можете избежать необходимости .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);
        }

    });

Ответ 3

вы также можете сохранить ссылку на 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');
},

Ответ 4

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

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

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

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

Ответ 5

Теперь 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));
 }

Ответ 6

Вам просто нужно связать ваше мероприятие

для 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});

}

Ответ 7

Теперь, реагируя на 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 или что-то еще для этого.

Ответ 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');
 },

Ответ 9

Здесь ЭТОТ контекст меняется. Используйте функцию стрелки, чтобы сохранить контекст класса 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');

Ответ 10

Если вы делаете это и у вас все еще есть проблема, моя проблема в том, что я вызывал две переменные с одинаковыми именами.

У меня были companies как объект, привезенный из Firebase, а затем я пытался вызвать this.setState({companies: companies}) - он не работал по понятным причинам.

Ответ 11

используйте функцию стрелки вместо переходных функций