ReactJS - เรนเดอร์ถูกเรียกเมื่อใดก็ตามที่เรียกว่า "setState"


503

React แสดงองค์ประกอบทั้งหมดและส่วนประกอบย่อยใหม่ทุกครั้งที่setStateเรียกใช้หรือไม่

ถ้าเป็นเช่นนั้นทำไม ฉันคิดว่าความคิดคือการตอบสนองเพียงเล็กน้อยตามที่ต้องการ - เมื่อรัฐเปลี่ยน

ในตัวอย่างง่ายๆดังต่อไปนี้คลาสทั้งสองจะแสดงอีกครั้งเมื่อมีการคลิกข้อความแม้ว่าข้อเท็จจริงที่ว่ารัฐจะไม่เปลี่ยนแปลงในการคลิกที่ตามมาเนื่องจากตัวจัดการ onClick ตั้งstateค่าเป็นค่าเดียวกันเสมอ:

this.setState({'test':'me'});

ฉันคาดว่าการแสดงผลจะเกิดขึ้นก็ต่อเมื่อstateข้อมูลมีการเปลี่ยนแปลง

นี่คือตัวอย่างรหัสในฐานะ JS Fiddleและตัวอย่างข้อมูลในตัว:

var TimeInChild = React.createClass({
    render: function() {
        var t = new Date().getTime();

        return (
            <p>Time in child:{t}</p>
        );
    }
});

var Main = React.createClass({
    onTest: function() {
        this.setState({'test':'me'});
    },

    render: function() {
        var currentTime = new Date().getTime();

        return (
            <div onClick={this.onTest}>
            <p>Time in main:{currentTime}</p>
            <p>Click me to update time</p>
            <TimeInChild/>
            </div>
        );
    }
});

ReactDOM.render(<Main/>, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.0/react-dom.min.js"></script>

[1]: http://jsfiddle.net/fp2tncmb/2/

ฉันมีปัญหาเดียวกันฉันไม่ทราบวิธีแก้ปัญหาที่แน่นอน แต่ฉันได้ล้างรหัสที่ไม่ต้องการจากส่วนประกอบมันเริ่มทำงานได้ตามปกติ
Jaison

ฉันอยากจะชี้ให้เห็นว่าในตัวอย่างของคุณ - เพราะองค์ประกอบของคุณได้รับการออกแบบไม่เพียง แต่พึ่งพารัฐที่ไม่ซ้ำกัน - การโทรsetState()ถึงแม้จะมีข้อมูลดัมมี่จะทำให้องค์ประกอบนั้นแสดงผลแตกต่างกัน จริง ๆ แล้วมันควรพยายามแสดงวัตถุของคุณอีกครั้งเมื่อบางสิ่งบางอย่างอาจมีการเปลี่ยนแปลงเพราะมิฉะนั้นการสาธิตของคุณ - สมมติว่ามันเป็นพฤติกรรมที่ตั้งใจ - ไม่ทำงาน!
Tadhg McDonald-Jensen

คุณอาจพูดถูก @ TadhgMcDonald-Jensen - แต่จากความเข้าใจของฉัน React จะแสดงผลเป็นครั้งแรก (เนื่องจากการเปลี่ยนแปลงสถานะจากสิ่งใดไปเป็นสิ่งที่ครั้งแรก) จากนั้นไม่ต้องแสดงอีกครั้ง แน่นอนว่าฉันผิด - เนื่องจากดูเหมือนว่า React ต้องการให้คุณเขียนshouldComponentUpdateวิธีการของคุณเองซึ่งฉันคิดว่าเวอร์ชันที่เรียบง่ายของมันจะต้องรวมอยู่ใน React แล้ว เสียงเหมือนเวอร์ชันเริ่มต้นที่รวมอยู่ในการตอบสนองจะส่งคืนtrueซึ่งบังคับให้ส่วนประกอบแสดงผลซ้ำทุกครั้ง
แบรดปาร์คส์

ใช่ แต่จะต้องสร้างการแสดงผลซ้ำใน DOM เสมือนเท่านั้นและจะเปลี่ยนเฉพาะ DOM ที่แท้จริงเท่านั้นหากส่วนประกอบนั้นมีการแสดงผลที่แตกต่างกัน การอัปเดตของ DOM เสมือนมักจะไม่สำคัญ (อย่างน้อยเมื่อเทียบกับการปรับเปลี่ยนสิ่งต่าง ๆ บนหน้าจอจริง) ดังนั้นการเรียกใช้เรนเดอร์ทุกครั้งที่อาจจำเป็นต้องอัปเดตแล้วเห็นว่าไม่มีการเปลี่ยนแปลงใด ๆ
Tadhg McDonald-Jensen

คำตอบ:


570

React แสดงส่วนประกอบและส่วนประกอบย่อยทั้งหมดใหม่ทุกครั้งที่ setState ถูกเรียกหรือไม่

โดยค่าเริ่มต้น - ใช่

มีบูลีนวิธีใดที่ควรคอมโพสิตentUpdate (วัตถุ nextProps วัตถุ nextState)แต่ละองค์ประกอบมีวิธีนี้และรับผิดชอบในการพิจารณา "ควรปรับปรุงองค์ประกอบ ( ฟังก์ชั่นการแสดงผลทำงาน)?" ทุกครั้งที่คุณเปลี่ยนสถานะหรือส่งอุปกรณ์ประกอบฉากใหม่จากองค์ประกอบหลัก

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

อ้างจากเอกสารอย่างเป็นทางการhttp://facebook.github.io/react/docs/component-specs.html#updating-shouldcomponentupdate

โดยค่าเริ่มต้นแล้วควร CompompentUpdate ส่งกลับค่าจริงเสมอเพื่อป้องกันข้อบกพร่องที่บอบบางเมื่อรัฐมีการเปลี่ยนแปลงในสถานที่ แต่ถ้าคุณระมัดระวังที่จะปฏิบัติต่อรัฐเป็นไม่เปลี่ยนรูปและเพื่ออ่านอย่างเดียวจากอุปกรณ์ประกอบฉากและรัฐในการแสดงผล () แล้วคุณสามารถแทนที่ การใช้งานที่เปรียบเทียบอุปกรณ์ประกอบฉากเก่าและรัฐกับการเปลี่ยนของพวกเขา

ส่วนถัดไปของคำถามของคุณ:

ถ้าเป็นเช่นนั้นทำไม ฉันคิดว่าความคิดคือการตอบสนองเพียงเล็กน้อยตามที่ต้องการ - เมื่อรัฐเปลี่ยนไป

มีสองขั้นตอนในสิ่งที่เราอาจเรียกว่า "เรนเดอร์":

  1. DOM เสมือนการแสดงผล: เมื่อทำให้วิธีการที่เรียกว่ามันจะกลับใหม่Dom เสมือนโครงสร้างของส่วนประกอบ ดังที่ฉันได้กล่าวไว้ก่อนหน้านี้เมธอดการแสดงผลนี้จะถูกเรียกเสมอเมื่อคุณเรียกใช้setState ()เนื่องจากshouldComponentUpdateจะส่งกลับค่าจริงเสมอตามค่าเริ่มต้น ดังนั้นโดยค่าเริ่มต้นไม่มีการเพิ่มประสิทธิภาพที่นี่ในการตอบสนอง

  2. การแสดงผล DOM แบบดั้งเดิม: การตอบสนองจะเปลี่ยนโหนด DOM จริงในเบราว์เซอร์ของคุณเฉพาะเมื่อมีการเปลี่ยนแปลงใน DOM เสมือนและมีความจำเป็นน้อย - นี่คือคุณลักษณะที่ยอดเยี่ยมของ React ซึ่งปรับการกลายพันธุ์ DOM จริงและทำให้การตอบสนองรวดเร็ว


47
คำอธิบายที่ดี! และสิ่งหนึ่งที่ทำให้ React ยังคงเปล่งประกายได้ในกรณีนี้คือมันไม่เปลี่ยนDOM จริงยกเว้นว่าจะมีการเปลี่ยนแปลง DOM เสมือน ดังนั้นถ้าฟังก์ชั่นการเรนเดอร์ของฉันส่งคืนสิ่งเดียวกัน 2 ครั้งติดต่อกัน DOM จริงไม่ได้เปลี่ยนแปลงเลย ... ขอบคุณ!
Brad Parks

1
ฉันคิดว่า@tungdถูกต้อง - ดูคำตอบของเขาด้านล่าง มันเป็นเรื่องของความสัมพันธ์ระหว่างผู้ปกครองกับลูก ตัวอย่างเช่นหากTimeInChildเป็นพี่น้องของMainดังนั้นการเรนเดอร์()จะไม่ถูกเรียกโดยไม่จำเป็น
gvlax

2
หากรัฐของคุณมีวัตถุจาวาสคริปต์ที่ค่อนข้างใหญ่ (คุณสมบัติทั้งหมด ~ 1k) ซึ่งแสดงผลเป็นต้นไม้ขนาดใหญ่ขององค์ประกอบ (รวม 100 ~) ... คุณควรปล่อยให้ฟังก์ชั่นการแสดงผลสร้างโดมเสมือนหรือคุณก่อนที่จะตั้งค่า เปรียบเทียบสถานะใหม่กับสถานะเก่าด้วยตนเองและโทรเฉพาะsetStateเมื่อคุณตรวจพบว่ามีความแตกต่างหรือไม่ ถ้าเป็นเช่นนั้นทำอย่างไรให้ดีที่สุด - เปรียบเทียบสตริง json สร้างและเปรียบเทียบ hash object, ... ?
Vincent Sels

1
@Petr แต่ถึงแม้ว่าจะมีปฏิกิริยาตอบโต้สร้าง dom เสมือนจริงถ้า dom เสมือนเดิมเหมือนกับ dom เสมือนจริงใหม่ dom ของเบราว์เซอร์จะไม่ถูกจับใช่ไหม?
Jaskey

3
ดูที่การใช้ React.PureComponent ( reactjs.org/docs/react-api.html#reactpurecomponent ) เพียงอัปเดต (แสดงผลซ้ำ) หากสถานะหรืออุปกรณ์ประกอบขององค์ประกอบมีการเปลี่ยนแปลงจริง อย่างไรก็ตามระวังว่าการเปรียบเทียบนั้นตื้น
debater

105

ไม่ตอบสนองจะไม่แสดงผลทุกอย่างเมื่อสถานะมีการเปลี่ยนแปลง

  • เมื่อใดก็ตามที่องค์ประกอบสกปรก (สถานะของมันเปลี่ยนแปลง) ส่วนประกอบนั้นและลูกของมันจะถูกเรนเดอร์ใหม่ สิ่งนี้ในระดับหนึ่งคือการสร้างการแสดงผลใหม่ให้น้อยที่สุดเท่าที่จะทำได้ เวลาเดียวที่ไม่มีการเรียกเรนเดอร์คือเมื่อบางสาขาถูกย้ายไปยังรูทอื่นซึ่งในทางทฤษฎีเราไม่จำเป็นต้องทำการเรนเดอร์อะไรอีก ในตัวอย่างของคุณTimeInChildเป็นองค์ประกอบลูกของMainดังนั้นจึงได้รับการแสดงผลอีกครั้งเมื่อสถานะของMainการเปลี่ยนแปลง

  • ปฏิกิริยาไม่ได้เปรียบเทียบข้อมูลสถานะ เมื่อsetStateมีการเรียกมันจะทำเครื่องหมายองค์ประกอบว่าสกปรก (ซึ่งหมายความว่าจะต้องมีการแสดงผลซ้ำ) สิ่งสำคัญที่ควรทราบคือถึงแม้ว่าrenderวิธีการของส่วนประกอบนั้นจะเรียกว่า DOM จริงจะได้รับการอัปเดตก็ต่อเมื่อเอาท์พุทแตกต่างจากทรี DOM ปัจจุบันเท่านั้น ในตัวอย่างของคุณแม้ว่าstateข้อมูลจะไม่เปลี่ยนแปลง แต่เวลาของการเปลี่ยนแปลงครั้งล่าสุดทำให้ Virtual DOM แตกต่างจาก DOM ของเอกสารดังนั้นเหตุใดจึงมีการอัปเดต HTML


ใช่นี่คือคำตอบที่ถูกต้อง เป็นการทดลองแก้ไขบรรทัดสุดท้ายเป็นReact.renderComponent (<div> <Main /> <TimeInChild /> </div>, document.body) ; และลบ<TimeInChild />ออกจากเนื้อหาของrender ()ขององค์ประกอบหลัก การแสดงผล ()ของTimeInChildจะไม่ถูกเรียกตามค่าเริ่มต้นเพราะไม่ใช่ลูกของMainอีกต่อไป
gvlax

ขอบคุณสิ่งเช่นนี้ค่อนข้างยุ่งยากนั่นคือสาเหตุที่ผู้เขียน React แนะนำให้ใช้render()วิธีการที่ "บริสุทธิ์" - เป็นอิสระจากสภาพภายนอก
tungd

3
@tungd มันหมายความว่าsome branch is moved to another rootอะไร? คุณโทรหาbranchอะไร คุณโทรหาrootอะไร
กรีน

2
ฉันชอบคำตอบที่ทำเครื่องหมายว่าเป็นคำตอบที่ถูกต้องจริงๆ ฉันเข้าใจการตีความ 'การแสดงผล' ของคุณในภาษา 'ของจริง' ด้านข้าง ... แต่ถ้าคุณดูจากด้านที่มีปฏิกิริยาตอบโต้คุณต้องบอกว่ามันแสดงผลอีกครั้ง โชคดีที่ฉลาดพอที่จะกำหนดสิ่งที่เปลี่ยนแปลงและอัพเดตสิ่งเหล่านี้เท่านั้น คำตอบนี้อาจสร้างความสับสนให้กับผู้ใช้ใหม่เป็นครั้งแรกที่คุณบอกว่าไม่แล้วคุณจะอธิบายว่าสิ่งต่าง ๆ ได้รับการแสดงผล ...
WiRa

1
@tungd ช่วยได้โปรดอธิบายคำถามของ Greenwhat does it mean some branch is moved to another root? What do you call branch? What do you call root?
madhu131313

7

แม้ว่าจะมีการระบุไว้ในคำตอบอื่น ๆ ที่นี่ส่วนประกอบควร:

  • ใช้shouldComponentUpdateเพื่อสร้างการแสดงผลเมื่อสถานะหรือคุณสมบัติเปลี่ยนไป

  • เปลี่ยนเป็นการขยายPureComponentซึ่งใช้shouldComponentUpdateวิธีการภายในเพื่อการเปรียบเทียบแบบตื้น

นี่คือตัวอย่างที่ใช้shouldComponentUpdateซึ่งทำงานเฉพาะกรณีการใช้งานและการสาธิตอย่างง่ายเท่านั้น เมื่อใช้สิ่งนี้องค์ประกอบจะไม่แสดงผลอีกครั้งในแต่ละคลิกและจะแสดงเมื่อแสดงครั้งแรกและหลังจากคลิกครั้งเดียว

var TimeInChild = React.createClass({
    render: function() {
        var t = new Date().getTime();

        return (
            <p>Time in child:{t}</p>
        );
    }
});

var Main = React.createClass({
    onTest: function() {
        this.setState({'test':'me'});
    },

    shouldComponentUpdate: function(nextProps, nextState) {
      if (this.state == null)
        return true;
  
      if (this.state.test == nextState.test)
        return false;
        
      return true;
  },

    render: function() {
        var currentTime = new Date().getTime();

        return (
            <div onClick={this.onTest}>
            <p>Time in main:{currentTime}</p>
            <p>Click me to update time</p>
            <TimeInChild/>
            </div>
        );
    }
});

ReactDOM.render(<Main/>, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.0/react-dom.min.js"></script>


6

ใช่. มันเรียกเมธอด render () ทุกครั้งที่เราเรียกใช้ setState แทนเมื่อ "shouldComponentUpdate" ส่งคืนค่าเท็จ


7
โปรดอธิบายเพิ่มเติม
Karthick Ramesh

3

เหตุผลอื่นสำหรับ "การอัพเดทที่สูญหาย" อาจเป็นสาเหตุต่อไป:

  • หากgetDerivedStateFromProps คงถูกกำหนดไว้แล้วมันจะวิ่งอยู่ในขั้นตอนการปรับปรุงทุกตามเอกสารที่เป็นทางการhttps://reactjs.org/docs/react-component.html#updating
  • ดังนั้นหากคุณค่าของรัฐนั้นมาจากอุปกรณ์ประกอบฉากในตอนเริ่มต้นมันจะถูกเขียนทับในการอัพเดททุกครั้ง

หากเป็นปัญหาจากนั้น U สามารถหลีกเลี่ยงการตั้งค่าสถานะในระหว่างการอัพเดตคุณควรตรวจสอบค่าพารามิเตอร์สถานะเช่นนี้

static getDerivedStateFromProps(props: TimeCorrectionProps, state: TimeCorrectionState): TimeCorrectionState {
   return state ? state : {disable: false, timeCorrection: props.timeCorrection};
}

โซลูชันอื่นคือการเพิ่มคุณสมบัติที่เตรียมใช้งานเป็นสถานะและตั้งค่าในครั้งแรก (ถ้าสถานะถูกเตรียมใช้งานเป็นค่าที่ไม่ใช่ null)


0

ไม่ใช่ส่วนประกอบทั้งหมด

stateในลักษณะองค์ประกอบเช่นแหล่งที่มาของน้ำตกของรัฐของ APP ทั้ง

ดังนั้นการเปลี่ยนแปลงจึงเกิดขึ้นจากที่ setState เรียก ต้นไม้แห่งrendersนั้นถูกเรียกมาจากที่นั่น หากคุณใช้องค์ประกอบที่บริสุทธิ์renderจะถูกข้ามไป

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.