Redux: Komponen tidak dirender ulang dengan connect ()

Dibuat pada 20 Agu 2015  ·  32Komentar  ·  Sumber: reduxjs/redux

Saya mengenal Redux dengan membaca dokumen dan saya mulai hanya memodifikasi contoh dunia nyata. Dalam kasus saya, saya telah menukar panggilan GitHub HTTP API untuk saya sendiri, dan menukar konsep Repo dan Pengguna dengan yang analog di lingkungan saya. Sejauh ini, seharusnya tidak terlalu berbeda, bukan?

Di sinilah saya mengalami masalah:

Panggilan API saya berhasil, respons JSON menjadi camelCased dan dinormalisasi, _but_ komponen saya tidak dirender ulang setelah permintaan API berhasil dilakukan. Segera setelah USER_REQUEST dikirim dan komponen Pengguna diperbarui untuk menampilkan "Memuat ...", USER_SUCCESS dikirim, dan status berikutnya berisi kunci entities terisi:

pasted_image_8_19_15__3_27_pm

Itu berarti masalahnya ada di middleware, bukan? Saya harus menyimpan entitas dari tindakan di toko, dan komponen harus mengamati perubahan dan render ulang, ya?

Di mana, dalam contoh dunia nyata, apakah data yang berhasil diambil dan dinormalisasi _actually_ dimasukkan ke dalam penyimpanan? Saya melihat di mana itu dinormalisasi, tetapi tidak di mana hasil normalisasi itu dikirim ke toko.

Terima kasih banyak!

question

Komentar yang paling membantu

Data ditetapkan / diperbarui / dihapus di toko melalui hasil penanganan tindakan di reduksi. Pengecil menerima status potongan aplikasi Anda saat ini, dan berharap mendapatkan kembali status baru. Salah satu alasan paling umum mengapa komponen Anda mungkin tidak dirender ulang adalah karena Anda mengubah status yang ada di peredam alih-alih mengembalikan salinan status baru dengan perubahan yang diperlukan (lihat bagian Pemecahan Masalah ). Saat Anda memutasi status yang ada secara langsung, Redux tidak mendeteksi perbedaan status dan tidak akan memberi tahu komponen Anda bahwa penyimpanan telah berubah.

Jadi saya pasti akan memeriksa pengurang Anda dan memastikan Anda tidak mengubah status yang ada. Semoga membantu!

Semua 32 komentar

Data ditetapkan / diperbarui / dihapus di toko melalui hasil penanganan tindakan di reduksi. Pengecil menerima status potongan aplikasi Anda saat ini, dan berharap mendapatkan kembali status baru. Salah satu alasan paling umum mengapa komponen Anda mungkin tidak dirender ulang adalah karena Anda mengubah status yang ada di peredam alih-alih mengembalikan salinan status baru dengan perubahan yang diperlukan (lihat bagian Pemecahan Masalah ). Saat Anda memutasi status yang ada secara langsung, Redux tidak mendeteksi perbedaan status dan tidak akan memberi tahu komponen Anda bahwa penyimpanan telah berubah.

Jadi saya pasti akan memeriksa pengurang Anda dan memastikan Anda tidak mengubah status yang ada. Semoga membantu!

Selain apa yang dikatakan @ernieturner , pastikan bahwa fungsi mapStateToProps di fungsi connect(mapStateToProps)(YourConnectedComponent) memetakan status yang relevan (dalam hal ini entities ) sehingga komponen yang terhubung mendengarkan ke bagian negara itu.

Juga, perlu diingat bahwa connect() melakukan perbandingan yang dangkal: https://github.com/rackt/react-redux/blob/d5bf492ee35ad1be8ffd5fa6be689cd74df3b41e/src/components/createConnect.js#L91

Sepertinya karena bentuk status terhubung Anda tidak berubah, maka diasumsikan untuk tidak melakukan pembaruan.

Ah, mengerti! mapStateToProps saya mencoba mengekstrak objek dari status, tetapi tidak menentukan kunci yang benar. Alih-alih mengekstrak entities.users[login] , saya menyetelnya ke entities.users[id] . Objek entities.users dikunci oleh string login , jadi ketika numerik id tidak ditemukan, tidak ada properti yang benar-benar berubah, jadi tidak diperlukan render ulang.

Terima kasih, semua, atas bantuannya! Saya sangat menghargai waktu yang Anda luangkan untuk membantu.

@timorr
Konyol sekali. Bagaimana cara memaksa connect() memeriksa perubahan mendalam? Jika tidak, seseorang harus membuat lusinan komponen kontainer untuk setiap tingkat status yang lebih dalam. Ini tidak normal

Anda benar-benar harus memiliki banyak komponen yang terhubung dalam aplikasi berukuran wajar apa pun. Jika Anda memiliki satu komponen terhubung tingkat atas, Anda akan merender ulang sebagian besar aplikasi Anda secara tidak perlu setiap kali status berubah sama sekali. Dan Anda akan menghindari salah satu pengoptimalan utama react-redux, yang hanya merender ulang ketika statusnya berlangganan perubahan.

Jika Anda perlu mengevaluasi kueri status yang kompleks, gunakan pemilihan ulang untuk mempercepatnya.

@timdorr @wzup

Mungkin berguna untuk mengklarifikasi bahwa menghubungkan di tempat yang lebih rendah di pohon tidak sepenuhnya diperlukan untuk menyelesaikan masalah perbandingan yang mendalam.

Menghubungkan di tempat yang lebih rendah dalam pohon komponen Anda mungkin memang membantu dengan _performance_, tetapi ini bukanlah solusi mendasar untuk masalah perbandingan mendalam. Solusi untuk masalah perbandingan mendalam adalah agar reduksi Anda memperbarui status secara permanen, sehingga perbandingan yang dangkal cukup untuk mengetahui kapan bagian yang lebih dalam dari status berubah.

Dengan kata lain, menghubungkan komponen tingkat atas ke bagian status bersarang yang besar berfungsi dengan baik _bahkan dengan perbandingan yang dangkal_, selama pereduksi Anda mengembalikan keadaan baru tanpa memutasi objek keadaan yang ada.

@naw Terima kasih atas tip itu. Berdasarkan itu saya akhirnya menambahkan peredam baru di mana saya mengalami masalah ini:

const reducers = combineReducers({
    //...bunch of reducers which always return objects of 3 or 4 properties that change sometimes but
    // the shape stays the same
    dataVersion: (state = Symbol(), action = {}) => {
        switch (action.type) {
            case SAME_ACTION_AS_THE_ONE_THAT_UPDATES_A_COMPLEX_PROPERTY:
            case ANOTHER_ACTION_THAT_ALSO_UPDATES_A_COMPLEX_PROPERTY:
                return Symbol():
            default:
                return state;
        }
    }
})

Ini masih terasa aneh bagiku. Jika saya punya peredam yang berbunyi:

    switch (action.type) {
       case UPDATE_THIS:
           return {a: action.a, b: action.b, c: action.c};
       default:
           return state;
     }

fakta bahwa saya mengembalikan objek yang baru diinisialisasi yang kebetulan memiliki bentuk yang sama seperti keadaan sebelumnya seharusnya cukup untuk memicu previousState !== newState . Jika saya seharusnya mengembalikan data yang tidak dapat diubah setiap kali, sepertinya lebih tidak efisien melakukan perbandingan bentuk yang dangkal daripada hanya identitasnya. (Dengan asumsi itulah yang sebenarnya dilakukan Redux. Fakta bahwa solusi Symbol saya berfungsi membuat saya berpikir itulah yang terjadi)

Dalam kasus khusus ini menambahkan lebih banyak komponen kontainer tidak menyelesaikan masalah. Sayangnya saya tidak dapat menautkan ke seluruh proyek github untuk klarifikasi karena ini adalah hal-hal sumber tertutup perusahaan saya yang saya tangani di sini.

@Zacqary Perbandingan keadaan saat ini dengan keadaan sebelumnya dilakukan dengan persamaan yang ketat (===). Perbandingan stateProps (hasil dari mapStateToProps) dilakukan dengan nilai yang dangkal.

@Tokopedia

mapStateToProps sedang "merakit" data untuk komponen Anda yang terhubung dengan menjangkau toko.

mapStateToProps menarik berbagai bagian keluar dari toko, dan menugaskannya ke kunci di objek stateProps .

Objek stateProps dihasilkan akan menjadi baru setiap saat (jadi identitasnya berubah), jadi kita tidak bisa begitu saja membandingkan identitas objek dari stateProps objek itu sendiri --- kita _have_ turun satu tingkat dan periksa identitas objek dari setiap nilai, di mana shallowEqual ikut bermain, seperti yang dikatakan @jimbolla .

Biasanya masalah muncul ketika Anda menulis reduksi sedemikian rupa sehingga identitas objek dari bagian negara _tidak berubah_, sementara beberapa atribut bersarang di dalamnya _does_ berubah. Ini adalah keadaan mutasi daripada mengembalikan keadaan baru tanpa perubahan, yang menyebabkan react-redux berpikir tidak ada yang berubah, padahal sebenarnya, sesuatu _did_ berubah.

fakta bahwa saya mengembalikan objek yang baru diinisialisasi yang kebetulan memiliki bentuk yang sama dengan keadaan sebelumnya sudah cukup untuk memicu Status sebelumnya! == Status baru.

Jika Anda mengembalikan objek baru untuk beberapa bagian status, dan mapStateToProps menggunakan potongan status tersebut sebagai nilai untuk salah satu kuncinya, maka react-redux akan melihat bahwa identitas objek berubah, dan akan tidak mencegah render ulang.

Apakah ada sesuatu yang spesifik yang tidak berfungsi seperti yang Anda harapkan? Saya tidak begitu yakin apa yang Anda maksud dengan solusi Symbol .

Perlu dipertimbangkan apakah komponen tambahan harus dipindahkan ke komponen connected() untuk mewarisi props. Saya mendapat masalah ini dengan mencoba connect() dua komponen, tetapi itu adalah kerumitan yang tidak perlu.

@ernieturner Saya tahu ini sudah tua tetapi Anda mungkin ingin memperbarui tautan Bagian Pemecahan Masalah Anda.

Bersulang

Saya punya masalah ini, yah tidak juga.
Saya menerapkan properti untuk menyatakan, seperti:

  constructor(props){
    this.state = {
      text: props.text
    }
  }

Dan itu tidak berhasil. Jadi saya menerapkan nilai langsung dari alat peraga seperti

{this.props.text}

Dan itu berfungsi dengan baik :)

@AlexanderKozhevin Kecuali saya salah, konstruktor Anda kehilangan super(props) .
Biasanya Anda bahkan tidak diperbolehkan menggunakan this sebelum menelepon super(props) .

@ cdubois-mh Benar, terima kasih untuk catatan.

Saya telah menulis peredam akar aplikasi yang dibuat khusus dan mendapatkan masalah itu.
Mengembalikan salinan seluruh negara bagian
{...state}
pada akhirnya peredam akar membantu saya

@daedalius Begitulah cara kerja peredam.
Ini harus mengembalikan status jika tindakan tidak relevan, atau mengembalikan salinan negara dengan nilai modifikasi yang diharapkan.

Jika Anda tidak mengembalikan salinan, maka react tidak akan selalu dapat mendeteksi bahwa ada nilai yang berubah.
(misalnya, jika Anda mengubah entri bersarang)

Periksa status ini:

{
    "clients": [
        { "name": "John", "cid": 4578 },
        { "name": "Alex", "cid": 5492 },
        { "name": "Bob",  "cid": 254 }
    ]
}

Jika Anda mengubah nama klien, tetapi tidak mengembalikan status tiruan, maka Anda tidak mengubah status, Anda memodifikasi objek dalam larik.

Objek itu sendiri masih akan memiliki referensi yang sama seperti sebelumnya, jadi react akan kehilangan perubahan.
Dengan mengembalikan klon, referensi status itu sendiri sekarang akan berbeda (karena ini adalah objek baru) dan react akan memulai seluruh proses rendering ulang.

Jika saya salah, tolong perbaiki saya, tapi begitulah cara saya memahami reduksi untuk bekerja.

Ya benar. Juga, @daedalius , perhatikan bahwa Anda tidak ingin _selalu_ membuat salinan dangkal dari state - Anda hanya boleh melakukannya jika ada yang berubah, jika tidak, Anda mungkin berakhir dengan rendering ulang komponen yang tidak perlu.

mengubah paksa

<AfterConnect
    _forceUpdate={Symbol()}
/>

@BrookShuihuaLee Apa maksudnya itu? Anda tidak memberikan informasi atau penjelasan apa pun tentang bagian kode Anda. Apa fungsinya? Mengapa ini relevan dengan diskusi saat ini?

@CedSharp Fungsi shallowEqual akan menampilkan false, jika kita menyetel _forceUpdate = {Symbol ()}

@BrookShuihuaLee Akan sangat bagus jika Anda bisa memasukkan penjelasan seperti ini di jawaban pertama Anda, dengan cara ini orang akan memahaminya dengan lebih baik ^^

@Ditjendikti

@BrookShuihuaLee : Sepertinya itu ide yang buruk. Mengapa Anda _ ingin_ melakukan itu?

@BrookShuihuaLee , saya setuju dengan @markerikson .
Ada alasan mengapa Anda seharusnya TIDAK mengembalikan klon saat status tidak berubah. Kecuali Anda dapat memberikan contoh yang valid di mana solusi Anda tampaknya diperlukan, saya sangat menyarankan agar Anda tidak membahas cara kerja Redux.

@Tokopedia
@tokopedia
Karena komponen AfterConnect akan sering dirender, sesuai desain. Tidak apa-apa untuk merendernya sekali lagi saat komponen induknya sedang dirender. Saya tidak ingin melakukan semua komputasi dalam komponen induk dan mengatur banyak props untuk AfterConnect.

@BrookShuihuaLee , maaf, tapi bagaimana relevansi komponen Anda dengan diskusi yang sedang berlangsung?
Jika saya mengerti dengan benar, Anda berbicara tentang komponen khusus Anda sendiri? ForceUpdate adalah sesuatu yang sangat tidak disarankan di dunia React dan saya tidak akan merekomendasikannya sebagai solusi.

Sebaliknya, Anda dapat menggunakan solusi yang dapat diamati di mana Anda mencari perubahan di tempat lain selain negara bagian? Saya tidak tahu mengapa Anda perlu merender ulang komponen tertentu tanpa statusnya diubah, tetapi jika Anda tahu mengapa harus dirender ulang, mungkin Anda bisa menggunakan sesuatu seperti mobx?
(https://github.com/mobxjs/mobx)

Saya mencoba memahami apa solusi Anda, dan mengapa itu berlaku dalam situasi ini.

@CedSharp Saya tidak bermaksud bahwa setiap orang harus menggunakan forceUpdate. Tetapi rekomendasinya adalah satu hal, pilihannya adalah hal lain. React juga menyimpan ReactComponent.prototype.forceUpdate untuk pengembang. Orang butuh pilihan.

Saya mencoba memberi pilihan lain. Mungkin, itu bukan pilihan terbaik.

Tidak optimal, tetapi saya mengalami masalah ini karena saya telah menyusun status aplikasi saya dengan tidak benar. Saya mengatasi masalah perbandingan yang dangkal dengan hanya memperbarui bidang dangkal di bagian negara bagian yang menyebabkan masalah bagi saya.

case SOME_ACTION:
      return {
        ...state,
        ts: (new Date).getTime(),
      };

IMO, bukan solusi yang baik, tetapi untuk siapa pun yang saat ini terikat (yaitu Anda harus merilis akhir hari), ini adalah perbaikan cepat dan Anda tidak perlu memaksakan render di tempat lain.

Kode tersebut harus difaktorisasi ulang sehingga perbandingan yang dangkal, bahkan pada status yang sangat bertingkat akan berfungsi.

Negara harus tidak berubah. Salin dalam keadaan di peredam, kemudian perbandingan dangkal dalam menghubungkan sudah cukup dan efektif.

Masalah saya adalah memiliki objek dengan array di dalamnya. Karena ini adalah perbandingan yang dangkal, ia tidak pernah mencapai objek dalam array anak. Saya menyelesaikannya dengan menggunakan list:state.obj.list daripada lists:state.obj di mapStateToProps 👍🏼

Saya baru saja mengalami masalah ini, dan setelah membaca beberapa balasan pertama, saya memeriksa peredam saya dan melihat saya salah ketik di sana. Saya mengganti nama properti dan memperbarui INITIAL_STATE tetapi saya lupa mengubah kunci yang sedang diperbarui oleh tindakan bermasalah ini.

Saya pada dasarnya memperbarui beberapa kunci acak yang tidak digunakan dalam komponen, jadi jelas, itu tidak akan dirender ulang ketika kunci acak ini diperbarui.

PERIKSA JENIS DI REDUCER ANDA! :)

Apakah halaman ini membantu?
0 / 5 - 0 peringkat