Lifting state up berarti memindahkan state dari komponen-komponen yang perlu berbagi state itu ke induk bersama terdekatnya. Induk menyimpan nilainya, mengirimnya ke bawah sebagai prop, dan mengirim fungsi yang dipanggil komponen anak untuk mengubahnya, sehingga setiap komponen anak merender dari satu salinan state yang sama.
Klik "Show" di panel mana pun: panel itu terbuka dan panel yang tadinya terbuka tertutup. Hanya satu panel yang bisa terbuka dalam satu waktu karena panel-panel itu tidak memutuskannya sendiri. App memegang openIndex, dan setiap Panel hanya menerima isOpen dan fungsi onOpen.
Masalahnya: state yang seharusnya sepakat
Mulai dari versi yang paling jelas, di mana setiap panel memiliki state isOpen sendiri. Setiap panel berfungsi, tetapi panel-panel itu tidak tahu apa-apa tentang satu sama lain, jadi tidak ada yang mencegah dua panel terbuka bersamaan.
Buka kedua panel: keduanya tetap terbuka bersamaan. State di React bersifat privat bagi komponen yang mendeklarasikannya, jadi komponen sibling tidak bisa membacanya atau mengatur ulangnya. Saat dua komponen perlu sepakat, state-nya harus tinggal di atas keduanya.
Mengangkat state dalam tiga langkah
Mengubah contoh kedua menjadi contoh pertama butuh tiga editan.
- Hapus state dari komponen anak. Hapus
useStatediPaneldan bacaisOpendari props sebagai gantinya. Komponen anak tidak lagi memutuskan apakah ia terbuka. - Kirim nilainya dan cara mengubahnya dari induk.
PanelmendapatisOpendan callbackonOpen. Komponen anak memanggilonOpen()saat tombolnya diklik; ia tidak tahu apa yang dilakukan induk dengan panggilan itu. - Tambahkan state ke induk bersama.
AppmendeklarasikanopenIndexdan mengubahnya menjadi props untuk setiap panel:isOpen={openIndex === 1}danonOpen={() => setOpenIndex(1)}.
Induk bersama terdekat adalah komponen terendah yang merender semua komponen yang membutuhkan state itu. Di sini, komponen itu adalah App. Jika panel-panelnya berada di dalam komponen Faq, state-nya akan ditaruh di Faq, bukan lebih tinggi.
Setelah diangkat, Panel dikontrol oleh induknya dalam arti yang sama dengan input terkontrol: ia menampilkan apa yang dikatakan props dan melaporkan perubahan lewat callback. Halaman tentang controlled vs uncontrolled component membahas ide yang sama untuk elemen form.
Satu sumber kebenaran
Saat dua bagian layar menampilkan fakta yang sama, simpan fakta itu sekali dan hitung semua yang lain darinya. Konverter suhu adalah contoh klasiknya: input Celsius dan Fahrenheit harus selalu sepakat, jadi keduanya tidak bisa menyimpan angkanya masing-masing.
Ketik di salah satu field, dan field lainnya ikut berubah. State-nya adalah satu fakta: angka yang terakhir diketik pengguna dan skala yang dipakainya. Field lainnya dihitung dari fakta itu saat render, jadi field yang sedang kamu ketik selalu menyimpan persis apa yang kamu ketik. Ganti state awalnya menjadi { value: '212', scale: 'f' }, dan pesan di bawah input berganti menjadi "Water boils."
Menyimpan angka Celsius dan angka Fahrenheit di dua state terpisah berarti setiap handler harus memperbarui keduanya, dan begitu ada satu handler yang lupa, kedua input itu tidak lagi sepakat. Satu nilai yang disimpan tidak mungkin berselisih dengan dirinya sendiri.
Mengirim setter ke bawah
Komponen anak hanya bisa mengubah state milik induk lewat fungsi yang diberikan induk. Kamu bisa mengirim setter-nya langsung (onSelect={setColor}) atau fungsi yang melakukan lebih banyak hal (onOpen={() => setOpenIndex(1)}). Memberi prop itu nama bergaya event seperti onSelect atau onChange, bukan setSelected, membuat komponen anak tidak perlu tahu bagaimana induk menyimpan nilainya, sehingga induk bisa mengubahnya nanti tanpa menyentuh komponen anak.
ColorPicker dan Preview tidak pernah berkomunikasi satu sama lain. Picker melaporkan pilihan ke atas, App menyimpannya, dan nilai baru mengalir ke bawah ke keduanya.
Kapan tidak perlu mengangkat state
Mengangkat state ada biayanya. Saat state tinggal di induk, setiap perubahan merender induk dan, secara default, semua komponen anaknya, termasuk yang tidak memakai state itu. Angkat hanya sampai induk bersama terdekat, dan biarkan state yang hanya dipakai satu komponen tetap di dalam komponen itu.
Ketik di kotaknya dan perhatikan konsol: hanya SearchBox yang merender di setiap ketukan tombol. Sekarang pindahkan query ke atas ke App dan kirim ke SearchBox sebagai props. Setiap ketukan tombol lalu juga mencatat ProductList, meskipun list itu tidak memakai query-nya. Jika list itu memang memfilter berdasarkan query, mengangkat state adalah keputusan yang tepat, karena kedua komponen sama-sama bergantung pada nilai yang sama.
Pertanyaan yang perlu diajukan adalah "siapa yang perlu membaca nilai ini?" Jika jawabannya satu komponen, state tetap di sana. Jika beberapa komponen, state dipindahkan ke induk bersama terdekat mereka.
Saat pengangkatan terlalu jauh
Kadang induk bersama terdekat berada jauh di atas tree, dan nilainya harus dikirim melewati beberapa komponen yang hanya meneruskannya. Itulah prop drilling. Beberapa lapisan props masih wajar dan mudah diikuti. Saat nilai yang sama berpindah melewati banyak lapisan, atau hampir setiap komponen membutuhkannya (user yang sedang login, tema, bahasa), baca dengan useContext, alih-alih mengirimnya secara manual. Context mengubah cara nilai itu sampai ke komponen anak; state-nya sendiri tetap tinggal di satu induk, jadi tetap terangkat.
Pertanyaan yang sering diajukan
Apa arti lifting state up di React?
Lifting state up adalah memindahkan sebagian state dari komponen-komponen yang memakainya ke induk bersama terdekatnya. Induk itu memiliki state-nya dan mengirim nilainya, ditambah fungsi untuk mengubahnya, ke komponen anak sebagai props.
Bagaimana dua komponen sibling berbagi state di React?
Komponen sibling tidak bisa membaca state satu sama lain. Taruh state-nya di induk yang sama, kirim nilainya ke keduanya, dan kirim setter (atau handler seperti onChange) ke komponen yang mengubahnya. Kedua sibling lalu merender dari nilai yang sama.
Bagaimana komponen anak memperbarui state milik induk?
Induk mengirim fungsi sebagai prop, misalnya onSelect={setSelected} atau onSelect={(id) => setSelected(id)}, lalu komponen anak memanggilnya. State tetap berada di induk; komponen anak hanya meminta perubahan.
Kapan sebaiknya saya tidak mengangkat state?
Saat hanya satu komponen yang memakai state itu. Mengangkatnya lebih tinggi dari yang diperlukan membuat induk merender di setiap perubahan dan menyebarkan props ke komponen yang tidak peduli. Simpan state sedekat mungkin dengan tempat ia dipakai.
Apa alternatif jika state terangkat terlalu jauh?
Jika kamu mendapati dirimu mengirim props yang sama melewati banyak lapisan, baca nilai bersama itu dengan context (useContext), atau ubah strukturnya agar komponen-komponen yang membutuhkannya berada lebih berdekatan.