Tìm kiếm trang web

javascript - Chuyện gì xảy ra khi sử dụng this.setState nhiều lần trong component React?


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 cho mục đích thảo luận). Tôi nghĩ rằng component sẽ được render hai lần nhưng rõ ràng nó chỉ được render một lần. Một kỳ vọng khác của tôi là có thể lần gọi thứ hai cho setState sẽ ghi đè lần đầu tiên, nhưng như bạn đoán - nó hoạt động tốt.

Liên kết đến 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 có nội dung 'render' xuất hiện ở mỗi lần render.

Bạn có thể giải thích tại sao nó hoạt động đúng không?

Câu trả lời #1

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

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

CẬP NHẬT

Với việc phát hành React v18.0, các cập nhật trạng thái trong các sự kiện không phải React (promises, setTimeout, v.v.) cũng được gom nhóm theo mặc định.

Tham khảo - https://reactjs.org/blog/2022/03/29/react-v18.html#new-feature-automatic-batching

Câu trả lời #2

Phương thức setState() không ngay lập tức cập nhật trạng thái của thành phần, nó chỉ đưa việc cập nhật vào hàng đợi để xử lý sau. React có thể gom nhiều yêu cầu cập nhật lại với nhau để làm cho việc render hiệu quả hơn. Do điều này, cần 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ụ, đoạn mã sau đây 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, thực hiện tất cả logic trong đối số callback:

//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ậ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 một cách nông với trạng thái thành phần trước đó. Phương thức này cập nhật trạng thái một cách không đồng bộ, vì vậy có một callback tùy chọn sẽ được gọi khi trạng thái đã được cập nhật hoàn toàn.

Ví dụ :

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

Đâ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>
     }
  }

Bài viết liên quan