Status ReactJS vs prop

121

Ini mungkin menginjak garis antara menjawab dan beropini, tapi saya akan bolak-balik tentang bagaimana menyusun komponen ReactJS saat kompleksitas tumbuh dan dapat menggunakan beberapa arah.

Berasal dari AngularJS, saya ingin meneruskan model saya ke komponen sebagai properti dan meminta komponen memodifikasi model secara langsung. Atau haruskah saya memisahkan model menjadi berbagai stateproperti dan mengumpulkannya kembali saat mengirim kembali ke hulu? Apa cara ReactJS?

Ambil contoh editor posting blog. Mencoba memodifikasi model secara langsung berakhir seperti:

var PostEditor = React.createClass({
  updateText: function(e) {
    var text = e.target.value;
    this.props.post.text = text;
    this.forceUpdate();
  },
  render: function() {
    return (
      <input value={this.props.post.text} onChange={this.updateText}/>
      <button onClick={this.props.post.save}/>Save</button>
    );
  }
});

Yang sepertinya salah.

Apakah ini lebih merupakan cara React untuk membuat textproperti model kita state, dan mengkompilasinya kembali ke dalam model sebelum menyimpan seperti:

var PostEditor = React.createClass({
  getInitialState: function() {
    return {
      text: ""
    };
  },
  componentWillMount: function() {
    this.setState({
      text: this.props.post.text
    });
  },
  updateText: function(e) {
    this.setState({
      text: e.target.value
    });
  },
  savePost: function() {
    this.props.post.text = this.state.text;
    this.props.post.save();
  },
  render: function() {
    return (
      <input value={this.state.text} onChange={this.updateText}/>
      <button onClick={this.savePost}/>Save</button>
    );
  }
});

Ini tidak memerlukan panggilan ke this.forceUpdate() , tetapi seiring berkembangnya model, (sebuah posting mungkin memiliki penulis, subjek, tag, komentar, peringkat, dll ...) komponen mulai menjadi sangat rumit.

Apakah metode pertama dengan ReactLink cara yang tepat?

nicholas
sumber

Jawaban:

64

Pendekatan kedua Anda lebih seperti itu. React tidak terlalu memedulikan model, melainkan peduli tentang nilai - nilai dan bagaimana mereka mengalir melalui aplikasi Anda. Idealnya, model posting Anda akan disimpan dalam satu komponen di root. Anda kemudian membuat komponen anak yang masing-masing mengkonsumsi bagian dari model.

Anda bisa meneruskan callback ke turunan yang perlu memodifikasi data Anda, dan memanggilnya dari komponen turunan.

Memodifikasi this.props atau this.state secara langsung bukanlah ide yang baik, karena React tidak akan dapat menerima perubahan tersebut. Itu karena React melakukan perbandingan dangkal dari post prop Anda untuk menentukan apakah itu telah berubah.

Saya membuat jsfiddle ini untuk menunjukkan bagaimana data dapat mengalir dari komponen luar ke dalam.

The handleClickMetode menunjukkan 3 cara untuk (im) benar negara update:

var Outer = React.createClass({

  getInitialState: function() {
    return {data: {value: 'at first, it works'}};
  },

  handleClick: function () {

    // 1. This doesn't work, render is not triggered.
    // Never set state directly because the updated values
    // can still be read, which can lead to unexpected behavior.

    this.state.data.value = 'but React will never know!';

    // 2. This works, because we use setState

    var newData = {value: 'it works 2'};
    this.setState({data: newData});

    // 3. Alternatively you can use React's immutability helpers
    // to update more complex models.
    // Read more: http://facebook.github.io/react/docs/update.html

    var newState = React.addons.update(this.state, {
      data: {value: {$set: 'it works'}}
    });
    this.setState(newState);
 },

  render: function() {
      return <Inner data={this.state.data} handleClick={this.handleClick} />;
  }
});
jxg
sumber
Tapi apa yang kita lakukan jika kita memiliki model buram, dengan fungsi manipulasi statusnya sendiri? Misalnya, anggap saja sebagai ganti textbidang, kita memiliki setText metode yang melakukan validasi dan beberapa hal lainnya. Saya dapat melihat metode (2) berfungsi jika setTextmurni dan mengembalikan contoh model baru. Namun, jika setTexthanya memperbarui keadaan batin kita masih perlu menelepon forceUpdate, bukan?
hugomg
1
Ya, Anda dapat menelepon forceUpdate, tetapi pada saat itu Anda "bocor" dari React. Mungkin lebih baik meneruskan setState()callback ke model buram untuk menghindari keharusan memicu rendering ulang secara manual.
jxg
Saya masih tidak yakin saya sepenuhnya memahami. Jadi komponen apa pun yang dimaksudkan untuk mengubah data perlu melakukan salinan mendalam dari props yang diteruskan? Kemudian ubah dan kirim salinan itu ke hulu agar tidak mengubah data asli? Akhirnya perubahan akan mengalir sampai ke root, di mana ia ditangani, dan seluruh aplikasi dirender? Apakah itu benar?
nicholas
97

Memperbarui 2016: React diubah, dan penjelasan "props vs state" menjadi sangat sederhana. Jika sebuah komponen perlu mengubah data - taruh dalam keadaan, jika tidak di props. Karena alat peraga sekarang hanya bisa dibaca .

Apa perbedaan yang tepat antara props dan state?

Anda dapat menemukan penjelasan yang bagus di sini (versi lengkap)

Mengubah alat peraga dan status

mrvol.dll
sumber
1
sebenarnya setProps () dapat mengubah props di dalam komponen dan memicu render ulang
WaiKit Kung
2
setPropssudah usang dan tidak boleh digunakan. Penggantian adalah untuk merender ulang komponen dan membiarkan React menangani perbedaannya.
jdmichal
Dan jika Anda mencari video penjelasan: youtube.com/watch?v=qh3dYM6Keuw
jaisonDavis
35

Dari React doc

props tidak dapat diubah: mereka diturunkan dari induk dan "dimiliki" oleh induknya. Untuk mengimplementasikan interaksi, kami memperkenalkan status yang bisa berubah ke komponen. this.state bersifat pribadi untuk komponen dan bisa diubah dengan memanggil this.setState (). Saat status diperbarui, komponen merender sendiri.

Dari TrySpace : saat props (atau status) diperbarui (melalui setProps / setState atau induk) komponen juga akan dirender ulang.

Fizer Khan
sumber
16

Bacaan dari Thinking in React :

Mari kita bahas masing-masing dan cari tahu yang mana yang merupakan negara bagian. Ajukan saja tiga pertanyaan tentang setiap bagian data:

  1. Apakah itu diteruskan dari orang tua melalui alat peraga? Jika demikian, mungkin itu bukan status.
  2. Apakah itu berubah seiring waktu? Jika tidak, mungkin itu bukan status.

  3. Dapatkah Anda menghitungnya berdasarkan status atau properti lain di komponen Anda? Jika demikian, itu bukan status.

onmyway133
sumber
13

Saya tidak yakin apakah saya menjawab pertanyaan Anda, tetapi saya telah menemukan bahwa, terutama dalam aplikasi besar / berkembang, pola Kontainer / Komponen bekerja dengan sangat baik.

Pada dasarnya Anda memiliki dua komponen React:

  • komponen tampilan "murni", yang berhubungan dengan gaya dan interaksi DOM;
  • komponen kontainer, yang berhubungan dengan mengakses / menyimpan data eksternal, mengelola status, dan merender komponen tampilan.

Contoh

NB Contoh ini mungkin terlalu sederhana untuk menggambarkan manfaat dari pola ini, karena cukup bertele-tele untuk kasus yang begitu sederhana.

/**
 * Container Component
 *
 *  - Manages component state
 *  - Does plumbing of data fetching/saving
 */

var PostEditorContainer = React.createClass({
  getInitialState: function() {
    return {
      text: ""
    };
  },

  componentWillMount: function() {
    this.setState({
      text: getPostText()
    });
  },

  updateText: function(text) {
    this.setState({
      text: text
    });
  },

  savePost: function() {
    savePostText(this.state.text);
  },

  render: function() {
    return (
      <PostEditor
        text={this.state.text}
        onChange={this.updateText.bind(this)}
        onSave={this.savePost.bind(this)}
      />
    );
  }
});


/**
 * Pure Display Component
 *
 *  - Calculates styling based on passed properties
 *  - Often just a render method
 *  - Uses methods passed in from container to announce changes
 */

var PostEditor = React.createClass({
  render: function() {
    return (
      <div>
        <input type="text" value={this.props.text} onChange={this.props.onChange} />
        <button type="button" onClick={this.props.onSave} />
      </div>
    );
  }
});

Manfaat

Dengan memisahkan logika tampilan dan manajemen data / status, Anda memiliki komponen tampilan yang dapat digunakan kembali yang:

  • dapat dengan mudah diulang dengan set props yang berbeda menggunakan sesuatu seperti react-component-playground
  • bisa dibungkus dengan wadah yang berbeda untuk perilaku yang berbeda (atau digabungkan dengan komponen lain untuk membangun bagian yang lebih besar dari aplikasi Anda

Anda juga memiliki komponen kontainer yang menangani semua komunikasi eksternal. Ini akan memudahkan Anda untuk bersikap fleksibel tentang cara Anda mengakses data jika Anda membuat perubahan serius di lain waktu *.

Pola ini juga membuat penulisan dan penerapan pengujian unit jauh lebih mudah.

Setelah mengulangi aplikasi React yang besar beberapa kali, saya menemukan bahwa pola ini membuat semuanya relatif tidak menyakitkan, terutama ketika Anda memiliki komponen yang lebih besar dengan gaya terhitung atau interaksi DOM yang rumit.

* Bacalah pola fluks, dan lihat Marty.js , yang sebagian besar menginspirasi jawaban ini (dan saya telah banyak menggunakan akhir-akhir ini) Redux (dan react-redux ), yang menerapkan pola ini dengan sangat baik.

Catatan untuk mereka yang membaca ini di 2018 atau lebih baru:

React telah berkembang cukup pesat sejak jawaban ini ditulis, terutama dengan diperkenalkannya Hooks . Namun, logika manajemen status yang mendasari dari contoh ini tetap sama, dan yang lebih penting, manfaat yang Anda peroleh dari memisahkan logika status dan presentasi Anda masih berlaku dengan cara yang sama.

Jim O'Brien
sumber
0

Saya pikir Anda menggunakan anti-pola yang telah dijelaskan Facebook di tautan ini

Inilah hal yang Anda temukan:

React.createClass({
  getInitialState: function() {
    return { value: { foo: 'bar' } };
  },

  onClick: function() {
    var value = this.state.value;
    value.foo += 'bar'; // ANTI-PATTERN!
    this.setState({ value: value });
  },

  render: function() {
    return (
      <div>
        <InnerComponent value={this.state.value} />
        <a onClick={this.onClick}>Click me</a>
      </div>
    );
  }
});

Pertama kali komponen dalam dirender, ini akan memiliki {foo: 'bar'} sebagai prop nilai. Jika pengguna mengklik jangkar, status komponen induk akan diperbarui menjadi {value: {foo: 'barbar'}}, memicu proses rendering ulang komponen dalam, yang akan menerima {foo: 'barbar'} sebagai nilai baru untuk prop.

Masalahnya adalah karena induk dan komponen dalam berbagi referensi ke objek yang sama, ketika objek dimutasi pada baris 2 dari fungsi onClick, prop yang dimiliki komponen dalam akan berubah. Jadi, ketika proses rendering ulang dimulai, dan shouldComponentUpdate dipanggil, this.props.value.foo akan sama dengan nextProps.value.foo, karena sebenarnya, this.props.value mereferensikan objek yang sama dengan nextProps.value.

Akibatnya, karena kita akan melewatkan perubahan pada prop dan sirkuit pendek proses rendering ulang, UI tidak akan diperbarui dari 'bar' ke 'barbar'

Alex Nguyen
sumber
Bisakah Anda juga memposting Innercomponentskode?
Abdullah Khan