Phản ứng this.setState không phải là một hàm


288

Tôi mới tham gia React và tôi đang cố gắng viết một ứng dụng hoạt động với API. Tôi cứ bị lỗi này:

TypeError: this.setState không phải là một hàm

khi tôi cố gắng xử lý phản hồi API. Tôi nghi ngờ có gì đó không ổn với ràng buộc này nhưng tôi không thể tìm ra cách khắc phục. Đây là mã của thành phần của tôi:

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

Câu trả lời:


348

Cuộc gọi lại được thực hiện trong một bối cảnh khác nhau. Bạn cần phải bindđến thisđể có thể tiếp cận bên trong gọi lại:

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: Có vẻ như bạn phải ràng buộc cả cuộc gọi initapicuộc gọi:

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, không. Không có đề cập đến ràng buộc trong hướng dẫn.
Tor Haugen

8
Có lẽ đã có 2,5 năm trước. 😁
Travis Reeder

1
Tôi đã sử dụng chức năng mũi tên để giải quyết vấn đề. Cảm ơn sự giúp đỡ
Tarun Nagpal

Ai đó có thể giải thích thêm về ý nghĩa của "cuộc gọi lại được thực hiện trong một bối cảnh khác" không?
SB

135

Bạn có thể tránh sự cần thiết của .bind (cái này) với chức năng mũi tên 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
Điều này hoạt động tốt. Trong thực tế, từ khóa của hàm không nên được hiển thị trong một tệp của es6.
JChen___

6
Câu trả lời của bạn đã giúp tôi :-) Sử dụng lớp ES6 và RN 0,34, tôi đã tìm thấy hai cách để liên kết "cái này" với chức năng gọi lại. 1) onChange={(checked) => this.toggleCheckbox()}, 2) onChange={this.toggleCheckbox.bind(this)}.
devdanke

Điều này tốt miễn là bạn không cần hỗ trợ các trình duyệt cũ.
Người dùng

Giải pháp hoàn hảo
Hitesh Sahu

2
GMsoF, hai giải pháp đó hoạt động vì a) khi bạn thực hiện .bind(this), nó đặt giá trị của thisbối cảnh cha mẹ this.toggleCheckbox()được gọi từ đâu, nếu không thissẽ đề cập đến nơi nó được thực thi. b) Giải pháp mũi tên chất béo hoạt động vì nó giữ được giá trị của this, vì vậy nó giúp bạn bằng cách không thay đổi mạnh mẽ giá trị của this. Trong JavaScript, thischỉ cần đề cập đến phạm vi hiện tại, vì vậy nếu bạn viết một hàm, thisthì đó là hàm đó. Nếu bạn đặt một chức năng bên trong nó, thislà bên trong chức năng con đó. Mũi tên béo giữ bối cảnh nơi được gọi từ
agm1984

37

bạn cũng có thể lưu một tham chiếu đến thistrước khi bạn gọi apiphương thức:

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 khuyên bạn nên ràng buộc điều này trong tất cả các phương pháp cần sử dụng phương pháp này classthay vì phương pháp này function.

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

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

Hoặc bạn có thể sử dụng arrow functionthay thế.


14

Bạn chỉ cần ràng buộc sự kiện của bạn

ví dụ

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

Bây giờ ES6 có chức năng mũi tên, nó thực sự hữu ích nếu bạn thực sự nhầm lẫn với biểu thức liên kết (này), bạn có thể thử chức năng mũi tên

Đây là cách tôi làm.

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

Bạn không cần gán giá trị này cho biến cục bộ nếu bạn sử dụng hàm mũi tên. Các hàm mũi tên sẽ tự động ràng buộc và bạn có thể tránh xa các vấn đề liên quan đến phạm vi.

Mã dưới đây giải thích cách sử dụng chức năng mũi tên trong các tình huống khác nhau

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

Bây giờ khi phản ứng với es6 / 7, bạn có thể liên kết chức năng với bối cảnh hiện tại với chức năng mũi tên như thế này, thực hiện yêu cầu và giải quyết các lời hứa như thế này:

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

Với phương pháp này, bạn có thể dễ dàng gọi hàm này trong thành phầnDidMount và đợi dữ liệu trước khi kết xuất html trong hàm kết xuất của bạn.

Tôi không biết quy mô dự án của bạn nhưng cá nhân tôi khuyên không nên sử dụng trạng thái hiện tại của thành phần để thao tác dữ liệu. Bạn nên sử dụng trạng thái bên ngoài như Redux hoặc Flux hoặc một cái gì đó khác cho điều đó.


5

sử dụng các hàm mũi tên, vì các hàm mũi tên trỏ đến phạm vi cha và điều này sẽ có sẵn. (thay thế kỹ thuật liên kết)


1
Một giải pháp ngắn gọn tuyệt vời
Chun

có vấn đề tương tự mặc dù được sử dụng để gọi phương thức là hàm mũi tên trong cuộc gọi, nhưng tôi đoán tôi phải thực hiện chức năng trạng thái như vậy, và chính xác những gì tôi đã làm
Carmine Tambascia

3

Ở đây bối cảnh NÀY đang được thay đổi. Sử dụng hàm mũi tên để giữ ngữ cảnh của lớp 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

Nếu bạn đang làm điều này và vẫn gặp sự cố, vấn đề của tôi là tôi đã gọi hai biến cùng tên.

Tôi đã có companiesmột đối tượng được mang đến từ Firebase, và sau đó đang cố gắng gọi this.setState({companies: companies})- nó không hoạt động vì những lý do rõ ràng.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.