ทำไมการเรียกใช้เมธอดปฏิกิริยา setState จึงไม่ทำให้เกิดการเปลี่ยนแปลงสถานะทันที


326

ฉันกำลังอ่านส่วนของฟอร์มเอกสารและลองใช้รหัสนี้เพื่อสาธิตonChangeการใช้งาน ( 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')
);

เมื่อฉันอัปเดต<input/>ค่าในเบราว์เซอร์ที่สองconsole.logภายในการhandleChangeโทรกลับพิมพ์เหมือนvalueครั้งแรกconsole.logทำไมฉันไม่เห็นผลลัพธ์this.setState({value: event.target.value})ในขอบเขตของการhandleChangeโทรกลับ


หากคุณกำลังใช้ตะขอจะดูที่วิธีการชุด useState ไม่ได้สะท้อนให้เห็นถึงการเปลี่ยนแปลงทันที
Emile Bergeron

คำตอบ:


615

จากเอกสารของ React :

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

หากคุณต้องการให้ฟังก์ชั่นที่จะดำเนินการหลังจากการเปลี่ยนแปลงสถานะที่เกิดขึ้นผ่านมันเป็นโทรกลับ

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

คำตอบที่ดี. การสังเกตที่ฉันต้องทำคือระวังการใช้ valueLink มันใช้งานได้ดีถ้าคุณไม่ต้องฟอร์แมต / ปิดบังอินพุต
Dherik

42
componentDidUpdateนอกจากนี้คุณยังอาจต้องการตรวจสอบ มันจะถูกเรียกหลังจากรัฐเปลี่ยนไป
Keysox

1
คำถามด่วนถ้าฉันอาจฉันเห็นเมื่อเราผ่านฟังก์ชั่นที่เราต้องการในการโทรกลับไปที่ setState ฉันหวังว่า func จะถูกดำเนินการก่อนที่จะเรียก render () แต่ฉันเห็นคำสั่งคือ setState () -> render () -> callStates 'setStates' () เป็นเรื่องปกติหรือไม่ ถ้าเราต้องการควบคุมการเรนเดอร์ของเราตามสิ่งที่เราทำในการติดต่อกลับ ควรจะมีการอัปเดตหรือไม่
semuzaboi

2
การเปลี่ยนสถานะขององค์ประกอบจะก่อให้เกิดการเรนเดอร์ใหม่เสมอเว้นแต่จะมีพฤติกรรมshouldComponentUpdateที่ระบุไว้เป็นอย่างอื่น คุณพยายามทำอะไรในการติดต่อกลับที่คุณส่งไปยังsetStateสิ่งที่คุณต้องการให้เกิดขึ้นก่อนที่จะเรนเดอร์ใหม่?
Michael Parker

4
...ทำไม? ใครบางคนสามารถพิสูจน์สิ่งนี้?
JackHasaKeyboard

49

ดังที่กล่าวไว้ในเอกสารตอบโต้ไม่มีการรับประกันsetStateว่าจะถูกยิงพร้อมกันดังนั้นคุณconsole.logอาจส่งคืนสถานะก่อนที่จะอัปเดต

setStateไมเคิลพาร์คเกอร์กล่าวผ่านการติดต่อกลับภายใน อีกวิธีหนึ่งในการจัดการตรรกะหลังจากการเปลี่ยนสถานะคือผ่านcomponentDidUpdateวิธีวงจรชีวิตซึ่งเป็นวิธีที่แนะนำใน React docs

โดยทั่วไปเราแนะนำให้ใช้ componentDidUpdate () สำหรับตรรกะดังกล่าวแทน

สิ่งนี้มีประโยชน์อย่างยิ่งเมื่ออาจมีการsetStateยิงต่อเนื่องและคุณต้องการยิงฟังก์ชั่นเดียวกันหลังจากเปลี่ยนสถานะทุกครั้ง แทนที่จะเพิ่มการโทรกลับไปยังแต่ละรายการsetStateคุณสามารถวางฟังก์ชันไว้ภายในcomponentDidUpdateด้วยตรรกะเฉพาะภายในหากจำเป็น

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

16

คุณสามารถลองใช้ ES7 async / await ตัวอย่างเช่นใช้ตัวอย่างของคุณ:

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

คำตอบของคุณแตกต่างจากคำตอบคุณภาพสูงอื่น ๆ อย่างไร
ทอด

9
คำตอบอื่น ๆ คือการใช้การโทรกลับใน setState () ฉันคิดว่าฉันใส่สิ่งนี้ไว้ที่นี่สำหรับผู้ที่ไม่มีกรณีใช้โทรกลับ ตัวอย่างเช่นเมื่อฉันประสบปัญหานี้ด้วยตนเองกรณีใช้งานของฉันเกี่ยวข้องกับตัวเรือนสวิตช์ในสถานะที่อัพเดตทันทีหลังจากตั้งค่า ดังนั้นการใช้ async / await จึงเป็นสิ่งที่ต้องการใช้โทรกลับ
kurokiiru

ผลกระทบนี้จะเกิดขึ้นหรือไม่หากฉันใช้การรออยู่ตลอดเวลาเมื่อฉันต้องการอัปเดตสถานะบางอย่างแล้วรอให้อัปเดต และถ้าฉันใส่ setstates หลาย ๆ ตัวในห่วงโซ่ข้างล่างอีกอันหนึ่งมันจะเรนเดอร์หลังจากแต่ละ setState update หรือไม่ หรือหลังการอัพเดต setState ครั้งล่าสุด
user2774480


ในสิ่งที่คุณขอให้ผู้ใช้ 2774480 ฉันเชื่อว่าทุกอย่างลงในกรณีการใช้งานเฉพาะเพื่อพิจารณาว่าการใช้งานใดที่จะใช้ หากมีการใช้หลาย setStates ในห่วงโซ่ประสิทธิภาพจะได้รับผลกระทบและใช่มันจะแสดงผลหลังจากแต่ละ setState แต่แก้ไขให้ถูกต้องถ้าฉันผิด
kurokiiru

8

ระวังวิธีวงจรชีวิตของการตอบสนอง!

ผมทำงานเป็นเวลาหลายชั่วโมงเพื่อจะพบว่าจะถูกเรียกว่าทุกครั้งหลังgetDerivedStateFromPropssetState()

😂


ความคิดเห็นของคุณบันทึกวันของฉัน: D
Csaba

-1

async-await ไวยากรณ์ทำงานได้อย่างสมบูรณ์แบบสำหรับสิ่งต่อไปนี้ ...

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

เพียงแค่วาง - this.setState ({data: value}) เป็นแบบอะซิงโครนัสในลักษณะที่หมายความว่ามันย้ายออกจาก Call Stack และกลับมาที่ Call Stack เท่านั้นเว้นแต่จะได้รับการแก้ไข

โปรดอ่านเกี่ยวกับ Event Loop เพื่อให้ได้ภาพที่ชัดเจนเกี่ยวกับลักษณะแบบอะซิงโครนัสใน JS และทำไมต้องใช้เวลาในการอัปเดต -

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

ดังนั้น -

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

เนื่องจากต้องใช้เวลาในการอัปเดต เพื่อให้บรรลุกระบวนการข้างต้น -

    this.setState({data:value},function () {
     console.log(this.state.data);
    });
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.