これは関数ではありません


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

編集:initapi呼び出しの両方をバインドする必要があるようです:

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年前にありました。😁
トラヴィスReederの

1
アロー関数を使用して問題を解決しました。助けてくれてありがとう
タルンNagpal

「コールバックは別のコンテキストで行われる」が意味することについて誰かがさらに詳しく説明できますか?
SB

135

ES6アロー関数を使用すると、.bind(this)の必要性を回避できます。

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を使用して、「this」をコールバック関数にバインドする2つの方法を見つけました。1) onChange={(checked) => this.toggleCheckbox()}、2)onChange={this.toggleCheckbox.bind(this)}
devdanke

古いブラウザをサポートする必要がない限り、これは良いことです。
ユーザーの

完璧なソリューション
Hitesh Sahu 2017

2
GMsoF、これらの2つのソリューションが機能するのは、a)を実行すると.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は、thisのclass代わりにthis を使用する必要があるすべてのメソッドでこれをバインドすることをお勧めします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に反応して、次のようなarrow関数を使用して関数を現在のコンテキストにバインドし、リクエストを作成して、次のようなpromiseを解決できます。

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

呼び出しで矢印関数としてメソッドを呼び出すために使用されたものと同じ問題がありますが、状態関数をそのように実装する必要があると思います。正確には、私が行ったこと
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

これを行っても問題が解決しない場合、私の問題は2つの変数を同じ名前で呼んでいたことです。

私はcompaniesFirebaseからオブジェクトとして持ち込まれ、それを呼び出そうとしましたthis.setState({companies: companies})-明らかな理由で機能しませんでした。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.