ตอบสนองคอมโพเนนต์ย่อยไม่อัปเดตหลังจากเปลี่ยนสถานะหลัก


111

ฉันกำลังพยายามสร้างส่วนประกอบ ApiWrapper ที่ดีเพื่อเติมข้อมูลในส่วนประกอบย่อยต่างๆ จากทุกสิ่งที่ฉันอ่านสิ่งนี้ควรใช้งานได้: https://jsfiddle.net/vinniejames/m1mesp6z/1/

class ApiWrapper extends React.Component {

  constructor(props) {
    super(props);

    this.state = {
      response: {
        "title": 'nothing fetched yet'
      }
    };
  }

  componentDidMount() {
    this._makeApiCall(this.props.endpoint);
  }

  _makeApiCall(endpoint) {
    fetch(endpoint).then(function(response) {
      this.setState({
        response: response
      });
    }.bind(this))
  }

  render() {
    return <Child data = {
      this.state.response
    }
    />;
  }
}

class Child extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      data: props.data
    };
  }

  render() {
    console.log(this.state.data, 'new data');
    return ( < span > {
      this.state.data.title
    } < /span>);
  };
}

var element = < ApiWrapper endpoint = "https://jsonplaceholder.typicode.com/posts/1" / > ;

ReactDOM.render(
  element,
  document.getElementById('container')
);

แต่ด้วยเหตุผลบางประการดูเหมือนว่าองค์ประกอบลูกจะไม่อัปเดตเมื่อสถานะหลักเปลี่ยนไป

ฉันขาดอะไรที่นี่?

คำตอบ:


207

มีสองปัญหาเกี่ยวกับรหัสของคุณ

สถานะเริ่มต้นขององค์ประกอบลูกของคุณถูกตั้งค่าจากอุปกรณ์ประกอบฉาก

this.state = {
  data: props.data
};

การอ้างอิงจากคำตอบ SOนี้:

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

ดังนั้นหากคุณไม่สามารถหลีกเลี่ยงสถานการณ์ดังกล่าวได้ทางออกที่ดีที่สุดคือใช้วิธีนี้componentWillReceivePropsเพื่อฟังอุปกรณ์ประกอบฉากใหม่ ๆ

การเพิ่มโค้ดด้านล่างในองค์ประกอบย่อยจะช่วยแก้ปัญหาของคุณด้วยการแสดงผลองค์ประกอบย่อย

componentWillReceiveProps(nextProps) {
  this.setState({ data: nextProps.data });  
}

ประเด็นที่สองคือไฟล์fetch.

_makeApiCall(endpoint) {
  fetch(endpoint)
    .then((response) => response.json())   // ----> you missed this part
    .then((response) => this.setState({ response }));
}

และนี่คือซอที่ใช้งานได้: https://jsfiddle.net/o8b04mLy/


1
"การเริ่มต้นสถานะตามอุปกรณ์ประกอบฉากเป็นเรื่องปกติหากคุณรู้ว่ากำลังทำอะไรอยู่" มีข้อเสียอื่น ๆ ในการตั้งค่าสถานะผ่านเสาหรือไม่นอกเหนือจากข้อเท็จจริงที่ว่าnextPropจะไม่ทำให้เกิดการเรนเดอร์ซ้ำโดยcomponentWillReceiveProps(nextProps)ไม่
Vinnie James

11
เท่าที่ฉันรู้ไม่มีข้อเสียอื่น ๆ แต่ในกรณีของคุณเราสามารถหลีกเลี่ยงการมีสถานะภายในองค์ประกอบย่อยได้อย่างชัดเจน ผู้ปกครองสามารถส่งผ่านข้อมูลเป็นอุปกรณ์ประกอบฉากได้และเมื่อผู้ปกครองแสดงผลซ้ำด้วยสถานะใหม่เด็กก็จะแสดงผลอีกครั้งด้วย (ด้วยอุปกรณ์ประกอบฉากใหม่) ที่จริงการรักษาสภาพภายในเด็กไม่จำเป็นที่นี่ ส่วนประกอบที่บริสุทธิ์ FTW!
Yadhu Kiran

7
สำหรับใครที่อ่านต่อจากนี้ตรวจสอบstatic getDerivedStateFromProps(nextProps, prevState) reactjs.org/docs/…
GoatsWearHats

4
มีวิธีแก้ปัญหาอื่นนอกเหนือจาก componentWillReceiveProps () หรือไม่เพราะตอนนี้เลิกใช้แล้ว
LearningMath

3
@LearningMath โปรดดูเอกสาร Reactล่าสุดซึ่งพูดถึงวิธีอื่น ๆ คุณอาจต้องพิจารณาตรรกะของคุณใหม่
Yadhu Kiran

1

มีบางสิ่งที่คุณต้องเปลี่ยนแปลง

เมื่อfetchได้รับการตอบสนองมันไม่ใช่ json ฉันกำลังมองหาวิธีรับ json นี้และฉันค้นพบลิงค์นี้

อีกด้านหนึ่งคุณต้องคิดว่าconstructorฟังก์ชันนั้นถูกเรียกใช้เพียงครั้งเดียว

ดังนั้นคุณต้องเปลี่ยนวิธีการดึงข้อมูลใน<Child>องค์ประกอบ

ฉันทิ้งโค้ดตัวอย่างไว้ที่นี่: https://jsfiddle.net/emq1ztqj/

ฉันหวังว่าจะช่วยได้


ขอบคุณ. แม้ว่าดูจากตัวอย่างที่คุณทำแล้วยังดูเหมือนว่าเด็กไม่เคยอัปเดต มีข้อเสนอแนะเกี่ยวกับวิธีเปลี่ยนวิธีรับข้อมูลของเด็กหรือไม่
Vinnie James

2
คุณแน่ใจไหม? ฉันเห็น<Child>ส่วนประกอบนั้นดึงข้อมูลใหม่จากhttps://jsonplaceholder.typicode.com/posts/1และทำการแสดงผลอีกครั้ง
slorenzo

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