ตอบสนอง 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));

แก้ไข: ดูเหมือนว่าคุณต้องผูกทั้ง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
ฉันใช้ฟังก์ชั่นลูกศรเพื่อแก้ไขปัญหา ขอบคุณสำหรับความช่วยเหลือ
Tarun Nagpal

ใครสามารถอธิบายเพิ่มเติมเกี่ยวกับสิ่งที่มีความหมายโดย "โทรกลับทำในบริบทที่แตกต่าง"?
SB

135

คุณสามารถหลีกเลี่ยงความต้องการ. ผูก (นี่) ด้วยฟังก์ชันลูกศร 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()}, onChange={this.toggleCheckbox.bind(this)}2)
devdanke

สิ่งนี้ดีตราบใดที่คุณไม่จำเป็นต้องสนับสนุนเบราว์เซอร์รุ่นเก่า
ผู้ใช้

โซลูชั่นที่สมบูรณ์แบบ
Hitesh Sahu

2
GMsoF โซลูชันทั้งสองทำงานเนื่องจาก) เมื่อคุณทำ.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');
},

32

React แนะนำให้ใช้วิธีนี้ในทุกวิธีที่จำเป็นต้องใช้classแทนตัว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 มีฟังก์ชั่นลูกศรมันมีประโยชน์จริง ๆ ถ้าคุณสับสนกับการแสดงออก (นี้) ผูกคุณสามารถลองใช้ฟังก์ชั่นลูกศร

นี่คือวิธีที่ฉันทำ

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

ด้วยวิธีนี้คุณสามารถเรียกใช้ฟังก์ชันนี้ใน componentDidMount และรอข้อมูลก่อนแสดง 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})- มันไม่ทำงานด้วยเหตุผลที่ชัดเจน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.