Tại sao gọi phương thức setState không làm thay đổi trạng thái ngay lập tức?


326

Tôi đang đọc phần Biểu mẫu củatài liệu và chỉ cần thử mã này để chứng minh onChangeviệc sử dụng ( JSBIN ).

var React= require('react');

var ControlledForm= React.createClass({
    getInitialState: function() {
        return {
            value: "initial value"
        };
    },

    handleChange: function(event) {
        console.log(this.state.value);
        this.setState({value: event.target.value});
        console.log(this.state.value);

    },

    render: function() {
        return (
            <input type="text" value={this.state.value} onChange={this.handleChange}/>
        );
    }
});

React.render(
    <ControlledForm/>,
  document.getElementById('mount')
);

Khi tôi cập nhật <input/>giá trị trong trình duyệt, cái thứ hai console.logtrong cuộc handleChangegọi lại sẽ in giống valuenhư lần đầu tiên console.log, Tại sao tôi không thể thấy kết quả this.setState({value: event.target.value})trong phạm vi handleChangegọi lại?


Câu trả lời:


615

Từ tài liệu của React :

setState()không ngay lập tức đột biến this.statenhưng tạo ra sự chuyển đổi trạng thái đang chờ xử lý. Truy cập this.statesau khi gọi phương thức này có thể có khả năng trả về giá trị hiện có. Không có gì đảm bảo cho hoạt động đồng bộ của các cuộc gọi đến setStatevà các cuộc gọi có thể được thực hiện theo đợt để tăng hiệu suất.

Nếu bạn muốn một chức năng được thực thi sau khi thay đổi trạng thái xảy ra, hãy chuyển nó dưới dạng gọi lại.

this.setState({value: event.target.value}, function () {
    console.log(this.state.value);
});

Câu trả lời tốt. Việc quan sát mà tôi cần làm là cẩn thận khi sử dụng valueLink. Nó hoạt động tốt nếu bạn không phải định dạng / mặt nạ đầu vào.
Dherik

42
Bạn cũng có thể muốn kiểm tra componentDidUpdate. Nó sẽ được gọi sau khi nhà nước đã thay đổi.
Keysox

1
Câu hỏi nhanh nếu tôi có thể, tôi thấy một khi chúng ta chuyển hàm mà chúng ta cần là gọi lại cho setState, tôi đã hy vọng rằng func sẽ được thực thi trước khi kết xuất () được gọi. Nhưng tôi thấy thứ tự là setState () -> render () -> gọi lại () của setStates. điều này có bình thường không Điều gì xảy ra nếu chúng ta muốn kiểm soát kết xuất của mình dựa trên những thứ chúng ta làm trong cuộc gọi lại? NênComponentUpdate ?
semuzaboi

2
Thay đổi trạng thái của một thành phần sẽ luôn kích hoạt kết xuất lại trừ khi có hành vi trong shouldComponentUpdateđó chỉ định khác. Chính xác thì bạn đang cố gắng làm gì trong cuộc gọi lại mà bạn đang chuyển đến setStatemà bạn muốn xảy ra trước khi kết xuất lại?
Michael Parker

4
...tại sao? Ai đó có thể biện minh cho điều này?
JackHasaPal

49

Như đã đề cập trong tài liệu React, không có gì đảm bảo setStateđược bắn đồng bộ, do đó bạn console.logcó thể trả lại trạng thái trước khi cập nhật.

Michael Parker đề cập đến việc thông qua một cuộc gọi lại trong setState. Một cách khác để xử lý logic sau khi thay đổi trạng thái là thông qua componentDidUpdatephương thức vòng đời, đây là phương pháp được đề xuất trong tài liệu React.

Nói chung, chúng tôi khuyên bạn nên sử dụng thành phầnDidUpdate () cho logic đó.

Điều này đặc biệt hữu ích khi có thể có các lần setStatebắn liên tiếp và bạn muốn thực hiện chức năng tương tự sau mỗi thay đổi trạng thái. Thay vì thêm một cuộc gọi lại cho mỗi setState, bạn có thể đặt chức năng bên trong componentDidUpdate, với logic cụ thể bên trong nếu cần thiết.

// example
componentDidUpdate(prevProps, prevState) {
  if (this.state.value > prevState.value) {
    this.foo();  
  }
}

16

Bạn có thể thử sử dụng ES7 async / await. Ví dụ sử dụng ví dụ của bạn:

handleChange: async function(event) {
    console.log(this.state.value);
    await this.setState({value: event.target.value});
    console.log(this.state.value);
}

Câu trả lời của bạn khác với câu trả lời chất lượng cao khác như thế nào?
tod

9
Câu trả lời khác liên quan đến việc sử dụng hàm gọi lại trong setState (). Tôi nghĩ rằng tôi đặt nó ở đây cho những người sử dụng trường hợp gọi lại không áp dụng. Chẳng hạn, khi tôi gặp phải vấn đề này, trường hợp sử dụng của tôi liên quan đến trường hợp chuyển đổi về trạng thái được cập nhật ngay sau khi đặt nó. Do đó, sử dụng async / await được ưu tiên sử dụng gọi lại.
Kurokiiru

điều này có ảnh hưởng đến hiệu suất không nếu tôi luôn sử dụng luôn chờ đợi khi tôi muốn cập nhật một số trạng thái và sau đó đợi nó cập nhật? Và nếu tôi đặt nhiều aSt setStates vào một chuỗi bên dưới một chuỗi khác, nó sẽ hiển thị sau mỗi lần cập nhật setState chứ? hoặc sau bản cập nhật setState cuối cùng?
dùng2774480


Về những gì bạn đang hỏi user2774480, tôi tin rằng tất cả đều thuộc về trường hợp sử dụng cụ thể để xác định nên sử dụng triển khai nào. Nếu nhiều setState được sử dụng trong một chuỗi, hiệu suất sẽ bị ảnh hưởng và vâng, nó sẽ hiển thị sau mỗi setState, nhưng hãy sửa tôi nếu tôi sai.
Kurokiiru


-1

async-await cú pháp hoạt động hoàn hảo cho một cái gì đó như sau ...

changeStateFunction = () => {
  // Some Worker..

  this.setState((prevState) => ({
  year: funcHandleYear(),
  month: funcHandleMonth()
}));

goNextMonth = async () => {
  await this.changeStateFunction();
  const history = createBrowserHistory();
  history.push(`/calendar?year=${this.state.year}&month=${this.state.month}`);
}

goPrevMonth = async () => {
  await this.changeStateFunction();
  const history = createBrowserHistory();
  history.push(`/calendar?year=${this.state.year}&month=${this.state.month}`);
}

-1

Đơn giản chỉ cần đặt - this.setState ({data: value}) về bản chất là không đồng bộ có nghĩa là nó di chuyển ra khỏi Ngăn xếp cuộc gọi và chỉ quay lại Ngăn xếp cuộc gọi trừ khi nó được giải quyết.

Vui lòng đọc về Vòng lặp sự kiện để có một bức tranh rõ ràng về bản chất không đồng bộ trong JS và lý do tại sao phải mất thời gian để cập nhật -

https://medium.com/front-end-weekly/javascript-event-loop-explained-4cd26af121d4

Vì thế -

    this.setState({data:value});
    console.log(this.state.data); // will give undefined or unupdated value

vì nó cần có thời gian để cập nhật. Để đạt được quy trình trên -

    this.setState({data:value},function () {
     console.log(this.state.data);
    });
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.