จะเกิดอะไรขึ้นเมื่อใช้ this.setState หลาย ๆ ครั้งใน React component?


105

ฉันต้องการตรวจสอบสิ่งที่เกิดขึ้นเมื่อคุณใช้ this.setState หลายครั้ง (2 ครั้งเพื่อประโยชน์ในการสนทนา) ฉันคิดว่าส่วนประกอบจะแสดงผลสองครั้ง แต่ดูเหมือนว่าจะแสดงผลเพียงครั้งเดียว ความคาดหวังอีกประการหนึ่งที่ฉันมีคือบางทีการเรียกที่สองสำหรับ setState จะทำงานเหนือรายการแรก แต่คุณเดาว่ามันใช้ได้

เชื่อมโยงไปยัง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')
);

ดังที่คุณจะเห็นการแจ้งเตือนที่ระบุว่า "แสดงผล" จะปรากฏขึ้นทุกครั้งที่เรนเดอร์

คุณมีคำอธิบายว่าเหตุใดจึงทำงานได้อย่างถูกต้อง?


2
การตอบสนองค่อนข้างฉลาดและจะแสดงผลอีกครั้งเมื่อสถานะที่จำเป็นในการเรนเดอร์เปลี่ยนไปเท่านั้น ในวิธีการเรนเดอร์ของคุณคุณใช้เพียงอย่างเดียวthis.state.alexจะเกิดอะไรขึ้นหากคุณเพิ่มองค์ประกอบที่ขึ้นอยู่กับthis.state.valueด้วย
Martin Wedvich

1
@MartinWedvich มันจะพังในกรณีที่ฉันขึ้นอยู่กับมัน เพื่อให้คุณมีเมธอด 'getInitialState' เพื่อตั้งค่าเริ่มต้นเพื่อไม่ให้แอปของคุณพัง
alexunder

1
เกี่ยวข้องและเป็นประโยชน์: stackoverflow.com/questions/48563650/…
andydavies

คำตอบ:


120

ตอบสนองการอัพเดตสถานะแบทช์ที่เกิดขึ้นในตัวจัดการเหตุการณ์และเมธอดวงจรชีวิต ดังนั้นหากคุณอัปเดตสถานะหลายครั้งใน<div onClick />ตัวจัดการ React จะรอให้การจัดการเหตุการณ์เสร็จสิ้นก่อนที่จะแสดงผล

เพื่อให้ชัดเจนสิ่งนี้ใช้ได้เฉพาะในตัวจัดการเหตุการณ์สังเคราะห์ที่ควบคุมด้วยปฏิกิริยาและวิธีวงจรชีวิตเท่านั้น การอัปเดตสถานะไม่รวมอยู่ใน AJAX และsetTimeoutตัวจัดการเหตุการณ์เป็นต้น


1
ขอบคุณมันสมเหตุสมผลแล้วมีความคิดยังไงบ้างเบื้องหลัง?
alexunder

15
เนื่องจาก React ควบคุมตัวจัดการเหตุการณ์สังเคราะห์ (เช่น<div onClick />) และวิธีวงจรชีวิตของคอมโพเนนต์อย่างสมบูรณ์จึงรู้ว่าสามารถเปลี่ยนลักษณะการทำงานได้อย่างปลอดภัยsetStateในช่วงระยะเวลาของการเรียกใช้การอัปเดตสถานะแบตช์และรอให้ล้างออก ในsetTimeoutทางตรงกันข้ามAJAX หรือตัวจัดการ React ไม่มีทางรู้ได้เลยว่าเมื่อตัวจัดการของเราดำเนินการเสร็จสิ้นแล้ว ในทางเทคนิค React จัดคิวการอัปเดตสถานะในทั้งสองกรณี แต่จะล้างทันทีนอกตัวจัดการที่ควบคุมด้วยปฏิกิริยา
Chris Gaudreau

2
@neurosnap ฉันไม่คิดว่าเอกสารจะลงรายละเอียดมากขนาดนี้ มันถูกกล่าวถึงเป็นนามธรรมในรัฐและระยะเวลาการแสดงและเอกสาร setState ดูรหัสสำหรับReactDefaultBatchingStrategyซึ่งปัจจุบันเป็นเพียงกลยุทธ์แบทช์ที่เป็นทางการเท่านั้น
Chris Gaudreau

2
@BenjaminToueg ฉันเชื่อว่าคุณสามารถใช้ReactDOM.unstable_batchedUpdates(function)เพื่อแบทช์setStateนอกตัวจัดการเหตุการณ์ตอบสนอง ตามความหมายของชื่อคุณจะทำให้การรับประกันของคุณเป็นโมฆะ
Chris Gaudreau

1
ดีเสมอที่จะอ้างอิงถึงผู้ชายคนนี้twitter.com/dan_abramov/status/887963264335872000?lang=en
Hertzel Guinness

37

เมธอด setState () ไม่ได้อัปเดตสถานะของคอมโพเนนต์ในทันที แต่จะทำให้การอัปเดตอยู่ในคิวเพื่อประมวลผลในภายหลัง React อาจรวมการร้องขอการอัพเดตหลายชุดเข้าด้วยกันเพื่อให้การแสดงผลมีประสิทธิภาพมากขึ้น ด้วยเหตุนี้จึงต้องมีการระมัดระวังเป็นพิเศษเมื่อคุณพยายามอัปเดตสถานะตามสถานะก่อนหน้าของส่วนประกอบ

ตัวอย่างเช่นรหัสต่อไปนี้จะเพิ่มแอตทริบิวต์ค่าสถานะทีละ 1 แม้ว่าจะถูกเรียก 4 ครั้ง:

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

ในการใช้สถานะหลังจากอัปเดตแล้วให้ใช้ตรรกะทั้งหมดในอาร์กิวเมนต์เรียกกลับ:

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

เมธอด setState (ตัวอัปเดต, [โทรกลับ]) สามารถใช้ฟังก์ชันตัวอัปเดตเป็นอาร์กิวเมนต์แรกเพื่ออัปเดตสถานะตามสถานะและคุณสมบัติก่อนหน้า ค่าที่ส่งคืนของฟังก์ชันตัวอัปเดตจะรวมเข้ากับสถานะส่วนประกอบก่อนหน้าอย่างตื้น ๆ วิธีนี้จะอัปเดตสถานะแบบอะซิงโครนัสดังนั้นจึงมีการเรียกกลับตัวเลือกที่จะถูกเรียกเมื่อสถานะเสร็จสิ้นการอัปเดตอย่างสมบูรณ์

ตัวอย่าง:

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

นี่คือตัวอย่างวิธีอัปเดตสถานะตามสถานะก่อนหน้า:

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

อ่าฉันไม่รู้เกี่ยวกับsetState(updater,[callback])มันเหมือนคำพูดที่ละเอียดอ่อนกว่าObject.assignขอบคุณสำหรับสิ่งนั้น!
AJ Venturella

1
@Biboswan สวัสดีฉันยังใหม่กับ React และต้องการถามคุณว่าจริงหรือไม่ที่ทั้งสี่ setStates ในเมธอด CompountDidMount ถูกรวมเข้าด้วยกันและจะดำเนินการเฉพาะ setState สุดท้ายเท่านั้น แต่อีก 3 รายการจะถูกละเว้น?
Dickens
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.