Bereaksi this.setState bukan fungsi

288

Saya baru di Bereaksi dan saya mencoba menulis aplikasi yang bekerja dengan API. Saya terus mendapatkan kesalahan ini:

TypeError: this.setState bukan fungsi

ketika saya mencoba menangani respons API. Saya curiga ada yang salah dengan pengikatan ini, tetapi saya tidak tahu cara memperbaikinya. Berikut kode komponen saya:

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

Jawaban:

348

Panggilan balik dibuat dalam konteks yang berbeda. Anda perlu binduntuk thisuntuk memiliki akses dalam callback:

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: Sepertinya Anda harus mengikat keduanya initdan apimenelepon:

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');
Davin Tryon
sumber
@ TravisReeder, tidak. Tidak disebutkan tentang binding dalam tutorial.
Tor Haugen
8
Mungkin ada 2,5 tahun yang lalu. 😁
Travis Reeder
1
Saya menggunakan fungsi panah untuk menyelesaikan masalah. Terima kasih atas bantuannya
Tarun Nagpal
Bisakah seseorang menjelaskan lebih jauh apa yang dimaksud dengan "panggilan balik dibuat dalam konteks yang berbeda"?
SB
135

Anda dapat menghindari kebutuhan .bind (this) dengan fungsi panah 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);
        }

    });
selamat tinggal
sumber
1
Ini bekerja dengan baik. Bahkan, kata kunci fungsi tidak boleh ditampilkan dalam file es6.
JChen___
6
Jawaban Anda membantu saya :-) Menggunakan kelas ES6 dan RN 0,34, saya menemukan dua cara untuk mengikat "ini" ke fungsi panggilan balik. 1) onChange={(checked) => this.toggleCheckbox()}, 2) onChange={this.toggleCheckbox.bind(this)}.
devdanke
Ini bagus asalkan Anda tidak perlu mendukung browser lama.
Pengguna
Solusi sempurna
Hitesh Sahu
2
GMsoF, kedua solusi itu bekerja karena a) ketika Anda melakukannya .bind(this), itu menetapkan nilai thisuntuk konteks induk dari mana this.toggleCheckbox()dipanggil, jika tidak thisakan merujuk ke tempat itu sebenarnya dieksekusi. b) Solusi panah gemuk berfungsi karena mempertahankan nilai this, sehingga membantu Anda dengan tidak mengubah nilai this. Dalam JavaScript, thiscukup merujuk ke ruang lingkup saat ini, jadi jika Anda menulis fungsi, thisapakah fungsi itu. Jika Anda meletakkan fungsi di dalamnya, thisada di dalam fungsi anak itu. Anak panah gemuk menjaga konteks tempat dipanggilnya
agm1984
37

Anda juga dapat menyimpan referensi thissebelum memohon apimetode:

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

Bereaksi merekomendasikan untuk mengikat ini dalam semua metode yang perlu menggunakan ini, classbukan ini dari diri function.

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

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

Atau Anda dapat menggunakannya arrow functionsebagai gantinya.

uruapanmexicansong
sumber
14

Anda hanya perlu mengikat acara Anda

untuk mantan

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

Sekarang ES6 memiliki fungsi panah, sangat membantu jika Anda benar-benar bingung dengan ekspresi bind (ini) Anda dapat mencoba fungsi panah

Inilah yang saya lakukan.

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
sumber
8

Anda tidak perlu menetapkan ini ke variabel lokal jika Anda menggunakan fungsi panah. Fungsi panah akan mengikat secara otomatis dan Anda dapat menghindari masalah terkait ruang lingkup.

Kode di bawah ini menjelaskan cara menggunakan fungsi panah di berbagai skenario

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');
 },
Hemadri Dasari
sumber
5

Sekarang dalam reaksi dengan es6 / 7 Anda dapat mengikat fungsi ke konteks saat ini dengan fungsi panah seperti ini, membuat permintaan dan menyelesaikan janji seperti ini:

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

Dengan metode ini Anda dapat dengan mudah memanggil fungsi ini di componentDidMount dan menunggu data sebelum membuat html Anda di fungsi render Anda.

Saya tidak tahu ukuran proyek Anda, tetapi saya pribadi menyarankan agar tidak menggunakan status komponen saat ini untuk memanipulasi data. Anda harus menggunakan kondisi eksternal seperti Redux atau Flux atau sesuatu yang lain untuk itu.

Quentin Malguy
sumber
5

gunakan fungsi panah, karena fungsi panah menunjuk ke lingkup induk dan ini akan tersedia. (pengganti teknik mengikat)

Pranav Kapoor
sumber
1
Solusi ringkas yang bagus
Chun
memiliki masalah yang sama meskipun digunakan untuk memanggil metode ini sebagai fungsi panah dalam panggilan, tapi saya rasa saya harus menerapkan fungsi negara seperti itu, dan persis apa yang telah saya lakukan
Carmine Tambascia
3

Di sini konteks ini berubah. Gunakan fungsi panah untuk menjaga konteks kelas Bereaksi.

        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');
Shubham Gupta
sumber
1

Jika Anda melakukan ini dan masih memiliki masalah, masalah saya adalah saya memanggil dua variabel dengan nama yang sama.

Saya memiliki companiesobjek yang dibawa dari Firebase, dan kemudian mencoba menelepon this.setState({companies: companies})- itu tidak berfungsi karena alasan yang jelas.

LukeVenter
sumber