React Child Component Not Updating After Parent State Change

109

Saya mencoba membuat komponen ApiWrapper yang bagus untuk mengisi data di berbagai komponen anak. Dari semua yang saya baca, ini seharusnya berfungsi: 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')
);

Namun untuk beberapa alasan, tampaknya komponen anak tidak diperbarui saat status induk berubah.

Apakah saya melewatkan sesuatu di sini?

Vinnie James
sumber

Jawaban:

205

Ada dua masalah dengan kode Anda.

Status awal komponen anak Anda disetel dari props.

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

Mengutip dari SO ini Jawabannya :

Meneruskan status awal ke komponen sebagai a propadalah anti-pola karena metode getInitialState(dalam kasus kami, konstuktor) hanya dipanggil saat pertama kali komponen dirender. Tidak lebih. Artinya, jika Anda merender ulang komponen itu dengan meneruskan nilai yang berbeda sebagai a prop, komponen tidak akan bereaksi dengan semestinya, karena komponen akan mempertahankan status sejak pertama kali dirender. Ini sangat rawan kesalahan.

Jadi jika Anda tidak dapat menghindari situasi seperti itu, solusi yang ideal adalah menggunakan metode componentWillReceivePropsuntuk mendengarkan alat peraga baru.

Menambahkan kode di bawah ini ke komponen anak Anda akan menyelesaikan masalah Anda dengan rendering ulang komponen Anak.

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

Masalah kedua adalah dengan fetch.

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

Dan ini biola yang berfungsi: https://jsfiddle.net/o8b04mLy/

Yadhu Kiran
sumber
1
"Tidak apa-apa untuk menginisialisasi status berdasarkan props jika Anda tahu apa yang Anda lakukan" Apakah ada kerugian lain untuk menyetel status melalui prop, selain fakta bahwa nextProptidak akan memicu render ulang tanpa componentWillReceiveProps(nextProps)?
Vinnie James
11
Sejauh yang saya tahu, tidak ada kerugian lainnya. Namun dalam kasus Anda, kami jelas dapat menghindari status di dalam komponen anak. Induk dapat meneruskan data sebagai props, dan ketika induk merender ulang dengan status barunya, anak juga akan merender ulang (dengan props baru). Sebenarnya menjaga keadaan dalam diri anak tidak perlu di sini. Komponen murni FTW!
Yadhu Kiran
7
Untuk siapa pun yang membaca mulai sekarang, periksa static getDerivedStateFromProps(nextProps, prevState) reactjs.org/docs/…
GoatsWearHats
4
Apakah ada solusi lain untuk ini selain componentWillReceiveProps () karena sekarang sudah tidak digunakan lagi?
LearningMath
3
@LearningMath harap lihat dokumen React terbaru yang membahas tentang cara alternatif. Anda mungkin harus mempertimbangkan kembali logika Anda.
Yadhu Kiran
1

Ada beberapa hal yang perlu Anda ubah.

Ketika fetchmendapat respon, itu bukan json. Saya sedang mencari bagaimana saya bisa mendapatkan json ini dan saya menemukan tautan ini .

Di sisi lain, Anda perlu berpikir bahwa constructorfungsi tersebut dipanggil hanya sekali.

Jadi, Anda perlu mengubah cara Anda mengambil data dalam <Child>komponen.

Di sini, saya meninggalkan kode contoh: https://jsfiddle.net/emq1ztqj/

Saya harap itu membantu.

slorenzo
sumber
Terima kasih. Meskipun, melihat contoh yang Anda buat, sepertinya Child tidak pernah memperbarui. Adakah saran tentang cara mengubah cara Child menerima data?
Vinnie James
2
Apakah kamu yakin Saya melihat <Child>komponen itu mengambil data baru dari https://jsonplaceholder.typicode.com/posts/1dan melakukan render ulang.
slorenzo
1
Ya, saya melihatnya berfungsi sekarang di OSX. iOS tidak memicu rendering ulang di browser aplikasi StackExchange
Vinnie James