Memperbarui status pada alat peraga berubah dalam Formulir Bereaksi

184

Saya mengalami masalah dengan formulir Bereaksi dan mengelola negara dengan benar. Saya memiliki bidang input waktu dalam formulir (dalam modal). Nilai awal diatur sebagai variabel keadaan di getInitialState, dan diteruskan dari komponen induk. Ini dengan sendirinya berfungsi dengan baik.

Masalahnya muncul ketika saya ingin memperbarui nilai start_time default melalui komponen induk. Pembaruan itu sendiri terjadi di komponen induk melalui setState start_time: new_time. Namun dalam formulir saya, nilai start_time default tidak pernah berubah, karena hanya didefinisikan sekali dalam getInitialState.

Saya telah mencoba menggunakan componentWillUpdateuntuk memaksa perubahan melalui negara setState start_time: next_props.start_time, yang benar-benar bekerja, tetapi memberi saya Uncaught RangeError: Maximum call stack size exceededkesalahan.

Jadi pertanyaan saya adalah, apa cara yang benar untuk memperbarui keadaan dalam kasus ini? Apakah saya memikirkan kesalahan ini?

Kode Saat Ini:

@ModalBody = React.createClass
  getInitialState: ->
    start_time: @props.start_time.format("HH:mm")

  #works but takes long and causes:
  #"Uncaught RangeError: Maximum call stack size exceeded"
  componentWillUpdate: (next_props, next_state) ->
    @setState(start_time: next_props.start_time.format("HH:mm"))

  fieldChanged: (fieldName, event) ->
    stateUpdate = {}
    stateUpdate[fieldName] = event.target.value
    @setState(stateUpdate)

  render: ->
    React.DOM.div
      className: "modal-body"
      React.DOM.form null,
        React.createElement FormLabelInputField,
          type: "time"
          id: "start_time"
          label_name: "Start Time"
          value: @state.start_time
          onChange: @fieldChanged.bind(null, "start_time”)

@FormLabelInputField = React.createClass
  render: ->
    React.DOM.div
      className: "form-group"
      React.DOM.label
        htmlFor: @props.id
        @props.label_name + ": "
      React.DOM.input
        className: "form-control"
        type: @props.type
        id: @props.id
        value: @props.value
        onChange: @props.onChange
David Basalla
sumber

Jawaban:

287

componentWillReceiveProps dideposisi sejak bereaksi 16: gunakan getDerivedStateFromProps sebagai gantinya

Jika saya mengerti dengan benar, Anda memiliki komponen induk yang start_timediturunkan ke ModalBodykomponen yang menetapkannya ke statusnya sendiri? Dan Anda ingin memperbarui waktu itu dari induk, bukan komponen anak.

Bereaksi memiliki beberapa tips untuk menangani skenario ini. (Catatan, ini adalah artikel lama yang telah dihapus dari web. Berikut tautan ke dokumen saat ini tentang alat peraga komponen ).

Menggunakan alat peraga untuk menghasilkan keadaan getInitialStatesering menyebabkan duplikasi "sumber kebenaran", yaitu di mana data sebenarnya berada. Ini karena getInitialStatehanya dipanggil ketika komponen pertama kali dibuat.

Bilamana memungkinkan, hitung nilai saat itu juga untuk memastikan bahwa mereka tidak keluar dari sinkronisasi nanti dan menyebabkan masalah pemeliharaan.

Pada dasarnya, setiap kali Anda menugaskan orang tua propske anak-anak, statemetode render tidak selalu dipanggil pada pembaruan prop. Anda harus menjalankannya secara manual, menggunakan componentWillReceivePropsmetode ini.

componentWillReceiveProps(nextProps) {
  // You don't have to do this check first, but it can help prevent an unneeded render
  if (nextProps.startTime !== this.state.startTime) {
    this.setState({ startTime: nextProps.startTime });
  }
}
Brad B
sumber
84
Tidak berlaku pada tanggal React 16
dude
7
@ Bung Ini belum ditinggalkan, apa yang Anda rujuk hanyalah kepala untuk referensi di masa mendatang. Saya kutip[..]going to be deprecated in the future
paddotk
7
@poepje Ini mungkin belum ditinggalkan, tetapi dipandang sebagai tidak aman oleh standar saat ini dan mungkin harus dihindari
unflores
12
Jadi, apa yang harus menjadi cara baru untuk melakukan ini setelah componentWillReceiveProps tidak digunakan lagi?
Boris D. Teoharov
5
@Boris Sekarang tim reaksi pada dasarnya menyuruhmu untuk diisi. Mereka memberi Anda metode baru, yang disebut getDerivedStateFromProps. Tangkapannya adalah bahwa ini adalah metode statis. Berarti Anda tidak dapat melakukan sesuatu yang tidak sinkron untuk memperbarui keadaan (karena Anda harus segera mengembalikan negara yang baru), Anda juga tidak dapat mengakses metode atau bidang kelas. Anda juga dapat menggunakan memoisasi, tetapi itu tidak cocok untuk setiap use case. Sekali lagi, tim reaksi ingin menekan cara mereka dalam melakukan sesuatu. Ini adalah keputusan desain yang sangat bodoh dan tidak mampu.
ig-dev
76

Rupanya semuanya berubah .... getDerivedStateFromProps () sekarang menjadi fungsi yang disukai.

class Component extends React.Component {
  static getDerivedStateFromProps(props, current_state) {
    if (current_state.value !== props.value) {
      return {
        value: props.value,
        computed_prop: heavy_computation(props.value)
      }
    }
    return null
  }
}

(kode di atas oleh danburzo @ github)

ErichBSchulz
sumber
7
FYI, Anda harus kembali juga nulljika tidak ada yang berubah begitu setelah Anda jika, Anda harus pergi denganreturn null
Ilgıt Yıldırım
@ IlgıtYıldırım - telah mengedit kode sejak 4 orang telah meningkatkan komentar Anda - apakah itu benar-benar membuat perbedaan?
ErichBSchulz
Ada sumber daya yang cukup bagus yang secara mendalam membahas berbagai opsi dan mengapa Anda akan menggunakan salah satu getDerivedStateFromPropsatau memo reactjs.org/blog/2018/06/07/...
unflores
2
getDerivedStateFromProps dipaksa statis. Berarti Anda tidak dapat melakukan sesuatu yang tidak sinkron untuk memperbarui keadaan, Anda juga tidak dapat mengakses metode atau bidang kelas. Sekali lagi, tim reaksi ingin menekan cara mereka dalam melakukan sesuatu. Ini adalah keputusan desain yang sangat bodoh dan tidak mampu.
ig-dev
39

componentWillReceiveProps sedang ditinggalkan karena menggunakannya "sering mengarah ke bug dan inkonsistensi".

Jika ada perubahan dari luar, pertimbangkan untuk mengatur ulang komponen anak sepenuhnyakey .

Memberikan keypenyangga ke komponen turunan memastikan bahwa kapan pun nilai keyperubahan dari luar, komponen ini dirender ulang. Misalnya,

<EmailInput
  defaultEmail={this.props.user.email}
  key={this.props.user.id}
/>

Tentang kinerjanya:

Walaupun ini mungkin terdengar lambat, perbedaan kinerja biasanya tidak signifikan. Menggunakan kunci bahkan bisa lebih cepat jika komponen memiliki logika berat yang berjalan pada pembaruan karena perbedaan dilewati untuk subtree itu.

Lucia
sumber
1
Kuncinya, rahasianya! Berfungsi sempurna dalam React 16 seperti yang disebutkan di atas
Darren Sweeney
kunci tidak akan berfungsi, jika itu objek dan Anda tidak memiliki string yang unik
user3468806
Kunci memang berfungsi untuk objek, saya berhasil. Tentu saja saya punya string unik untuk kunci.
tsujp
@ user3468806 Jika ini bukan objek kompleks dengan referensi eksternal, Anda dapat menggunakan JSON.stringify(myObject)untuk mendapatkan kunci unik dari objek Anda.
Roy Prins
24

Ada juga componentDidUpdate yang tersedia.

Signatur fungsi:

componentDidUpdate(prevProps, prevState, snapshot)

Gunakan ini sebagai peluang untuk beroperasi pada DOM ketika komponen telah diperbarui. Tidak dipanggil pada awal render.

Sampai jumpa Mungkin Anda Tidak Perlu Turunkan Artikel Negara , yang menjelaskan Anti-Pola untuk keduanya componentDidUpdatedan getDerivedStateFromProps. Saya merasa sangat berguna.

arminfro
sumber
Saya akhirnya menggunakan componentDidUpdatekarena sederhana dan lebih cocok untuk kebanyakan kasus.
KeitelDOG
14

Cara kait baru untuk melakukan ini adalah dengan menggunakan useEffect alih-alih componentWillReceiveProps dengan cara lama:

componentWillReceiveProps(nextProps) {
  // You don't have to do this check first, but it can help prevent an unneeded render
  if (nextProps.startTime !== this.state.startTime) {
    this.setState({ startTime: nextProps.startTime });
  }
}

menjadi yang berikut dalam komponen yang digerakkan oleh kait fungsional:

// store the startTime prop in local state
const [startTime, setStartTime] = useState(props.startTime)
// 
useEffect(() => {
  if (props.startTime !== startTime) {
    setStartTime(props.startTime);
  }
}, [props.startTime]);

kami mengatur negara menggunakan setState, menggunakan useEffect kami memeriksa perubahan pada prop yang ditentukan, dan mengambil tindakan untuk memperbarui status pada perubahan prop.

MMO
sumber
5

Anda Mungkin Tidak Perlu Status Turunan

1. Atur kunci dari induk

Ketika suatu kunci berubah, Bereaksi akan membuat instance komponen baru alih-alih memperbarui yang sekarang. Kunci biasanya digunakan untuk daftar dinamis tetapi juga berguna di sini.

2. Gunakan getDerivedStateFromProps/componentWillReceiveProps

Jika kunci tidak berfungsi karena alasan tertentu (mungkin komponennya sangat mahal untuk diinisialisasi)

Dengan menggunakan getDerivedStateFromPropsAnda dapat mengatur ulang bagian mana pun dari negara tetapi tampaknya sedikit buggy saat ini (v16.7) !, lihat tautan di atas untuk penggunaan

Ghominejad
sumber
2

Dari dokumentasi reaksi: https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html

Menghapus status saat perubahan props adalah Pola Anti

Sejak Bereaksi 16, componentWillReceiveProps sudah tidak digunakan lagi. Dari dokumentasi reaksi, pendekatan yang direkomendasikan dalam kasus ini adalah penggunaan

  1. Sepenuhnya dikendalikan komponen: yang ParentComponentsatu ModalBodyakan memiliki start_timenegara. Ini bukan pendekatan pilihan saya dalam hal ini karena saya pikir modal harus memiliki keadaan ini.
  2. Komponen yang sepenuhnya tidak terkendali dengan kunci: ini adalah pendekatan yang saya sukai. Contoh dari dokumentasi reaksi: https://codesandbox.io/s/6v1znlxyxn . Anda sepenuhnya akan memiliki start_timestatus dari Anda ModalBodydan menggunakan getInitialStateseperti yang telah Anda lakukan. Untuk mengatur ulang start_timekeadaan, Anda cukup mengubah kunci dariParentComponent
Lu Tran
sumber
0

Gunakan Memoize

Derivasi keadaan op adalah manipulasi langsung alat peraga, tanpa derivasi sejati yang diperlukan. Dengan kata lain, jika Anda memiliki properti yang dapat digunakan atau diubah secara langsung, Anda tidak perlu menyimpannya di negara bagian .

Mengingat bahwa nilai state start_timehanyalah prop start_time.format("HH:mm"), informasi yang terkandung dalam prop sudah cukup untuk memperbarui komponen.

Namun jika Anda hanya ingin memanggil format pada perubahan prop, cara yang benar untuk melakukan ini per dokumentasi terbaru akan melalui Memoize: https://reactjs.org/blog/2018/06/07/you-probably-dont- need-diturunkan-state.html # what-about-memoization

DannyMoshe
sumber
-1

Saya pikir menggunakan ref aman untuk saya, tidak perlu peduli dengan beberapa metode di atas.

class Company extends XComponent {
    constructor(props) {
        super(props);
        this.data = {};
    }
    fetchData(data) {
        this.resetState(data);
    }
    render() {
        return (
            <Input ref={c => this.data['name'] = c} type="text" className="form-control" />
        );
    }
}
class XComponent extends Component {
    resetState(obj) {
        for (var property in obj) {
            if (obj.hasOwnProperty(property) && typeof this.data[property] !== 'undefined') {
                if ( obj[property] !== this.data[property].state.value )
                    this.data[property].setState({value: obj[property]});
                else continue;
            }
            continue;
        }
    }
}
Mungkin Cuaca VN
sumber
Saya pikir respons ini samar (kode hampir tidak dapat dibaca dan tanpa penjelasan / menghubungkan ke masalah OP) dan tidak menangani masalah OP, yaitu bagaimana menangani keadaan awal.
netchkin