Render ulang komponen React saat prop berubah

90

Saya mencoba memisahkan komponen presentasi dari komponen wadah. Saya memiliki a SitesTabledan a SitesTableContainer. Penampung bertanggung jawab untuk memicu tindakan redux untuk mengambil situs yang sesuai berdasarkan pengguna saat ini.

Masalahnya adalah pengguna saat ini diambil secara asinkron, setelah komponen penampung dirender pada awalnya. Ini berarti bahwa komponen penampung tidak mengetahui bahwa ia perlu menjalankan ulang kode dalam componentDidMountfungsinya yang akan memperbarui data untuk dikirim ke SitesTable. Saya rasa saya perlu merender ulang komponen kontainer ketika salah satu alat peraga (pengguna) berubah. Bagaimana cara melakukannya dengan benar?

class SitesTableContainer extends React.Component {
    static get propTypes() {
      return {
        sites: React.PropTypes.object,
        user: React.PropTypes.object,
        isManager: React.PropTypes.boolean
      }
     }

    componentDidMount() {
      if (this.props.isManager) {
        this.props.dispatch(actions.fetchAllSites())
      } else {
        const currentUserId = this.props.user.get('id')
        this.props.dispatch(actions.fetchUsersSites(currentUserId))
      }  
    }

    render() {
      return <SitesTable sites={this.props.sites}/>
    }
}

function mapStateToProps(state) {
  const user = userUtils.getCurrentUser(state)

  return {
    sites: state.get('sites'),
    user,
    isManager: userUtils.isManager(user)
  }
}

export default connect(mapStateToProps)(SitesTableContainer);
David
sumber
Anda memang memiliki beberapa fungsi lain yang tersedia, seperti componentDidUpdate, atau mungkin yang Anda cari, componentWillReceiveProps (nextProps) jika Anda ingin mengaktifkan sesuatu ketika props berubah
maka
Mengapa Anda perlu merender ulang SitesTable jika tidak mengubah alat peraga?
QoP
@QoP tindakan yang dikirim componentDidMountakan mengubah sitesnode dalam status aplikasi, yang diteruskan ke SitesTable. Node SitesStable sitesakan berubah.
David
Oh, saya mengerti, saya akan menulis jawabannya.
QoP
1
Bagaimana mencapai ini dalam komponen fungsional
yaswanthkoneri

Jawaban:

115

Anda harus menambahkan kondisi dalam componentDidUpdatemetode Anda .

Contoh yang digunakan fast-deep-equaluntuk membandingkan objek.

import equal from 'fast-deep-equal'

...

constructor(){
  this.updateUser = this.updateUser.bind(this);
}  

componentDidMount() {
  this.updateUser();
}

componentDidUpdate(prevProps) {
  if(!equal(this.props.user, prevProps.user)) // Check if it's a new user, you can also use some unique property, like the ID  (this.props.user.id !== prevProps.user.id)
  {
    this.updateUser();
  }
} 

updateUser() {
  if (this.props.isManager) {
    this.props.dispatch(actions.fetchAllSites())
  } else {
    const currentUserId = this.props.user.get('id')
    this.props.dispatch(actions.fetchUsersSites(currentUserId))
  }  
}

Menggunakan Hooks (React 16.8.0+)

import React, { useEffect } from 'react';

const SitesTableContainer = ({
  user,
  isManager,
  dispatch,
  sites,
}) => {
  useEffect(() => {
    if(isManager) {
      dispatch(actions.fetchAllSites())
    } else {
      const currentUserId = user.get('id')
      dispatch(actions.fetchUsersSites(currentUserId))
    }
  }, [user]); 

  return (
    return <SitesTable sites={sites}/>
  )

}

Jika prop Anda membandingkan adalah obyek atau array, Anda harus menggunakan useDeepCompareEffectbukan useEffect.

QoP
sumber
Perhatikan bahwa JSON.stringify hanya dapat digunakan untuk jenis perbandingan ini, jika stabil (menurut spesifikasinya tidak), sehingga menghasilkan keluaran yang sama untuk masukan yang sama. Saya merekomendasikan untuk membandingkan properti id dari objek pengguna, atau meneruskan userId-s di props, dan membandingkannya, untuk menghindari pemuatan ulang yang tidak perlu.
László Kardinál
4
Harap dicatat bahwa metode siklus hidup componentWillReceiveProps tidak digunakan lagi dan kemungkinan besar akan dihapus di React 17. Menggunakan kombinasi componentDidUpdate dan metode getDerivedStateFromProps baru adalah strategi yang disarankan dari tim pengembang React. Lebih dalam posting blog mereka: reactjs.org/blog/2018/03/27/update-on-async-rendering.html
michaelpoltorak
@QoP Contoh kedua, dengan React Hooks, apakah ia akan melepas dan memasang kembali setiap kali ada userperubahan? Seberapa mahal ini?
Robotron
30

ComponentWillReceiveProps()akan dihentikan di masa mendatang karena bug dan inkonsistensi. Solusi alternatif untuk merender ulang komponen pada perubahan props adalah dengan menggunakan ComponentDidUpdate()dan ShouldComponentUpdate().

ComponentDidUpdate()dipanggil setiap kali komponen diperbarui DAN jika ShouldComponentUpdate()mengembalikan nilai true (Jika ShouldComponentUpdate()tidak ditentukan, ia kembali truesecara default).

shouldComponentUpdate(nextProps){
    return nextProps.changedProp !== this.state.changedProp;
}

componentDidUpdate(props){
    // Desired operations: ex setting state
}

Perilaku yang sama ini dapat dicapai hanya dengan menggunakan ComponentDidUpdate()metode dengan menyertakan pernyataan bersyarat di dalamnya.

componentDidUpdate(prevProps){
    if(prevProps.changedProp !== this.props.changedProp){
        this.setState({          
            changedProp: this.props.changedProp
        });
    }
}

Jika seseorang mencoba untuk mengatur keadaan tanpa bersyarat atau tanpa mendefinisikan ShouldComponentUpdate()komponen akan merender ulang tanpa batas

ob1
sumber
2
Jawaban ini perlu diberi suara positif (setidaknya untuk saat ini) karena componentWillReceivePropsakan dihentikan dan disarankan untuk tidak digunakan.
AnBisw
Bentuk kedua (pernyataan bersyarat di dalam componentDidUpdate) berfungsi untuk saya karena saya ingin perubahan status lainnya masih terjadi, misalnya menutup pesan flash.
Otak Kecil
11

Anda bisa menggunakan KEYkunci unik (kombinasi data) yang berubah dengan props, dan komponen itu akan dirender dengan props yang diperbarui.

Rafi
sumber
4
componentWillReceiveProps(nextProps) { // your code here}

Saya pikir itu adalah acara yang Anda butuhkan. componentWillReceivePropsterpicu setiap kali komponen Anda menerima sesuatu melalui props. Dari sana Anda dapat memeriksa lalu melakukan apa pun yang ingin Anda lakukan.

mr.b.
sumber
11
componentWillReceivePropsdeprecated *
Maihan Nijat
2

Saya akan merekomendasikan untuk melihat jawaban saya ini, dan melihat apakah itu relevan dengan apa yang Anda lakukan. Jika saya memahami masalah Anda yang sebenarnya, Anda hanya tidak menggunakan tindakan asinkron Anda dengan benar dan memperbarui "penyimpanan" redux, yang secara otomatis akan memperbarui komponen Anda dengan alat peraga yang baru.

Bagian kode Anda ini:

componentDidMount() {
      if (this.props.isManager) {
        this.props.dispatch(actions.fetchAllSites())
      } else {
        const currentUserId = this.props.user.get('id')
        this.props.dispatch(actions.fetchUsersSites(currentUserId))
      }  
    }

Seharusnya tidak memicu dalam sebuah komponen, itu harus ditangani setelah menjalankan permintaan pertama Anda.

Lihat contoh ini dari redux-thunk :

function makeASandwichWithSecretSauce(forPerson) {

  // Invert control!
  // Return a function that accepts `dispatch` so we can dispatch later.
  // Thunk middleware knows how to turn thunk async actions into actions.

  return function (dispatch) {
    return fetchSecretSauce().then(
      sauce => dispatch(makeASandwich(forPerson, sauce)),
      error => dispatch(apologize('The Sandwich Shop', forPerson, error))
    );
  };
}

Anda tidak perlu menggunakan redux-thunk, tetapi ini akan membantu Anda bernalar tentang skenario seperti ini dan menulis kode yang cocok.

TameBadger
sumber
benar, saya mengerti. Tapi di mana tepatnya Anda mengirimkan makeASandwichWithSecretSauce komponen Anda?
David
Saya akan menautkan Anda ke repo dengan contoh yang relevan, apakah Anda menggunakan react-router dengan aplikasi Anda?
TameBadger
@David juga akan menghargai tautan ke contoh itu, pada dasarnya saya memiliki masalah yang sama.
SamYoungNY
0

Metode ramah untuk digunakan adalah sebagai berikut, setelah pembaruan prop, secara otomatis akan merender komponen:

render {

let textWhenComponentUpdate = this.props.text 

return (
<View>
  <Text>{textWhenComponentUpdate}</Text>
</View>
)

}
0x01 Otak
sumber