Điều gì xảy ra khi sử dụng this.setState nhiều lần trong thành phần React?


101

Tôi muốn kiểm tra điều gì sẽ xảy ra khi bạn sử dụng this.setState nhiều lần (2 lần vì lợi ích của cuộc thảo luận). Tôi nghĩ rằng thành phần sẽ được hiển thị hai lần nhưng dường như nó chỉ được hiển thị một lần. Một kỳ vọng khác mà tôi có là có thể cuộc gọi thứ hai cho setState sẽ chạy trên lệnh đầu tiên, nhưng bạn đoán nó - hoạt động tốt.

Liên kết đến một JSfiddle

var Hello = React.createClass({
  render: function() {
    return (
      <div>
        <div>Hello {this.props.name}</div>
        <CheckBox />
      </div>
    );
  }
});

var CheckBox = React.createClass({
  getInitialState: function() {
    return {
      alex: 0
    };
  },

  handleChange: function(event) {
    this.setState({
      value: event.target.value
    });
    this.setState({
      alex: 5
    });
  },

  render: function() {
    alert('render');
    return (
      <div>
        <label htmlFor="alex">Alex</label>
        <input type="checkbox" onChange={this.handleChange} name="alex" />
        <div>{this.state.alex}</div>
      </div>
    );
  }
});

ReactDOM.render(
  <Hello name="World" />,
  document.getElementById('container')
);

Như bạn sẽ thấy, một cảnh báo cho biết 'kết xuất' sẽ bật lên trên mỗi kết xuất.

Bạn có lời giải thích tại sao nó hoạt động bình thường không?


2
React khá thông minh và sẽ chỉ render khi trạng thái cần render bị thay đổi. Trong phương thức kết xuất mà bạn chỉ sử dụng this.state.alex- điều gì sẽ xảy ra nếu bạn thêm một phần tử phụ thuộc vào this.state.value?
Martin Wedvich

1
@MartinWedvich Nó sẽ hỏng trong trường hợp tôi phụ thuộc vào nó. Đối với điều đó, bạn có phương thức 'getInitialState' - để đặt các giá trị mặc định để ứng dụng của bạn không bị hỏng.
alexunder

1
có liên quan và hữu ích: stackoverflow.com/questions/48563650/…
andydavies

Câu trả lời:


117

Phản ứng hàng loạt cập nhật trạng thái xảy ra trong trình xử lý sự kiện và phương thức vòng đời. Do đó, nếu bạn cập nhật trạng thái nhiều lần trong một <div onClick />trình xử lý, React sẽ đợi quá trình xử lý sự kiện kết thúc trước khi hiển thị lại.

Nói rõ hơn, điều này chỉ hoạt động trong các phương thức vòng đời và trình xử lý sự kiện tổng hợp do React kiểm soát. setTimeoutVí dụ: cập nhật trạng thái không được cập nhật trong AJAX và trình xử lý sự kiện.


1
Cảm ơn, nó có ý nghĩa, bất kỳ ý tưởng làm thế nào nó được thực hiện ở hậu trường?
alexunder

13
Vì React hoàn toàn kiểm soát các trình xử lý sự kiện tổng hợp (như <div onClick />) và các phương thức vòng đời thành phần, nên nó biết rằng nó có thể thay đổi hành vi một cách an toàn setStatetrong suốt thời gian gọi cập nhật trạng thái hàng loạt và chờ xả. setTimeoutNgược lại, trong AJAX hoặc trình xử lý, React không có cách nào để biết khi nào trình xử lý của chúng ta đã thực thi xong. Về mặt kỹ thuật, React xếp hàng đợi các cập nhật trạng thái trong cả hai trường hợp, nhưng sẽ trôi ngay ra bên ngoài trình xử lý do React kiểm soát.
Chris Gaudreau

1
@neurosnap Tôi không nghĩ tài liệu đi sâu vào chi tiết về vấn đề này. Nó được đề cập một cách trừu tượng trong phần State and Lifecycle và các tài liệu setState . Xem mã cho ReactDefaultBatchingStrategy , hiện là chiến lược phân phối chính thức duy nhất.
Chris Gaudreau

2
@BenjaminToueg Tôi tin rằng bạn có thể sử dụng ReactDOM.unstable_batchedUpdates(function)hàng loạt setStatebên ngoài trình xử lý sự kiện React. Như tên của nó, bạn sẽ mất hiệu lực bảo hành.
Chris Gaudreau

1
luôn tốt khi giới thiệu anh chàng này trên twitter.com/dan_abramov/status/887963264335872000?lang=en
Hertzel Guinness

33

Phương thức setState () không cập nhật ngay trạng thái của thành phần, nó chỉ đặt bản cập nhật vào hàng đợi để xử lý sau. React có thể gộp nhiều yêu cầu cập nhật lại với nhau để kết xuất hiệu quả hơn. Do đó, phải thực hiện các biện pháp phòng ngừa đặc biệt khi bạn cố gắng cập nhật trạng thái dựa trên trạng thái trước đó của thành phần.

Ví dụ: mã sau sẽ chỉ tăng thuộc tính giá trị trạng thái lên 1 mặc dù nó được gọi 4 lần:

 class Counter extends React.Component{
   constructor(props){
     super(props)
    //initial state set up
     this.state = {value:0}
   }
   componentDidMount(){
    //updating state
    this.setState({value:this.state.value+1})
    this.setState({value:this.state.value+1})
    this.setState({value:this.state.value+1})
    this.setState({value:this.state.value+1})
   }
   render(){
    return <div>Message:{this.state.value}</div>
   }
}

Để sử dụng một trạng thái sau khi nó đã được cập nhật, hãy thực hiện tất cả logic trong đối số gọi lại:

//this.state.count is originally 0
this.setState({count:42}, () => {
  console.log(this.state.count)
//outputs 42
})

Phương thức setState (updater, [callback]) có thể nhận một hàm updater làm đối số đầu tiên của nó để cập nhật trạng thái dựa trên trạng thái và thuộc tính trước đó. Giá trị trả về của hàm updater sẽ được hợp nhất nông với trạng thái thành phần trước đó. Phương thức cập nhật trạng thái không đồng bộ, do đó, có một tùy chọn gọi lại sẽ được gọi sau khi trạng thái cập nhật xong hoàn toàn.

Thí dụ:

this.setState((prevState, props) => { 
return {attribute:"value"}
})

Dưới đây là một ví dụ về cách cập nhật trạng thái dựa trên trạng thái trước đó:

    class Counter extends React.Component{
      constructor(props) {
        super(props)
    //initial state set up
        this.state = {message:"initial message"}
    }
      componentDidMount() {
    //updating state
        this.setState((prevState, props) => {
          return {message: prevState.message + '!'}
        })
     }
     render(){
       return <div>Message:{this.state.message}</div>
     }
  }

Ah, tôi không biết về setState(updater,[callback]), nó giống như một Object.assignlời cảm ơn ít dài dòng hơn cho điều đó!
AJ Venturella

1
@Biboswan, xin chào, tôi mới làm quen với React và muốn hỏi bạn rằng có đúng là tất cả bốn setState bên trong phương thức CompountDidMount đều được hợp nhất và CHỈ CÀI ĐẶT SetState sẽ được thực thi nhưng 3 cái còn lại sẽ bị bỏ qua?
Dickens
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.