Lifecycle komponen React punya tiga fase: mounting (komponen muncul di layar untuk pertama kali), updating (komponen dirender lagi karena state, props, atau context-nya berubah), dan unmounting (komponen dihapus). Di function component, kamu merespons fase-fase ini dengan useEffect: effect dengan [] berjalan setelah mounting, effect dengan dependensi berjalan setelah update yang mengubah dependensinya, dan fungsi yang dikembalikan effect berjalan saat unmount.
Konsol di bawah pratinjau menampilkan setiap fase saat terjadi.
Konsol diawali dengan render, count = 0 dan mounted. Setiap klik pada Count mencatat sebuah render dan sebuah update. Unmount mencatat unmounted; Mount again memulai lagi dari count = 0, karena state ikut dibuang bersama komponennya.
Mounting
Sebuah komponen mount saat React merendernya pertama kali di posisi tertentu dalam tree. React memanggil fungsinya, membuat node DOM dari JSX yang dikembalikannya, memasukkannya, membiarkan browser menggambar, lalu menjalankan effect komponen itu.
useEffect(fn, []) adalah tempat untuk memulai hal-hal yang harus ada selama komponen tampil di layar: subscription, timer, koneksi, request data pertama. Effect ini berjalan setelah browser menggambar, jadi tidak pernah menunda frame pertama.
Updating
Sebuah komponen melakukan update (dirender lagi) saat:
- state miliknya sendiri berubah,
- komponen induknya dirender lagi (secara default, setiap anak juga ikut dirender, entah props-nya berubah atau tidak),
- context yang dibacanya berubah.
Setelah setiap update, React menjalankan effect yang dependensinya berubah. Sebelum menjalankan sebuah effect lagi, React menjalankan cleanup sebelumnya dari effect itu, jadi subscription lama ditutup sebelum yang baru dibuka. Halaman rendering membahas lebih rinci apa saja yang memicu render.
Unmounting
Sebuah komponen unmount saat induknya berhenti merendernya (seperti yang dilakukan {show && <Counter />} di atas), saat key-nya berubah, atau saat root-nya sendiri unmount. React menghapus node DOM-nya, menjalankan cleanup dari setiap effect, dan membuang state-nya. Jika komponen itu muncul lagi nanti, ia adalah instance yang benar-benar baru.
Induk dan anak
React merender dari atas ke bawah, tetapi menjalankan effect dari bawah ke atas. Saat induk dan anak mount bersamaan, effect anak berjalan lebih dulu, jadi effect induk bisa mengandalkan bahwa anak-anaknya sudah siap sepenuhnya.
Saat mount, tercatat render Parent, render Child, Child effect, Parent effect. Tekan Remove, dan cleanup berjalan dengan urutan terbalik: Parent cleanup, lalu Child cleanup.
Fase render dan commit
Setiap update melewati dua fase:
- Fase render. React memanggil komponenmu untuk menghitung JSX baru dan membandingkannya dengan hasil sebelumnya. Belum ada yang berubah di layar. React bisa menjeda pekerjaan ini, menjalankannya dua kali di StrictMode, atau membuangnya, dan itulah sebabnya rendering harus murni: tanpa request, tanpa subscription, tanpa mengubah variabel di luar.
- Fase commit. React menerapkan perbedaannya ke DOM, memasang ref, dan menjalankan callback
useLayoutEffect, semuanya sebelum browser menggambar. CallbackuseEffectberjalan setelahnya, biasanya setelah browser selesai menggambar. Jika update dipicu oleh klik atau input diskrit lain, React justru menjalankannya langsung setelah layout effect, sehingga hasilnya siap sebelum event berikutnya.
Setiap klik mencatat tiga baris dengan urutan yang sama. Effect ini tidak punya array dependensi, jadi berjalan setelah setiap render. Tambahkan [] sebagai argumen kedua di keduanya, dan effect hanya berjalan setelah render pertama.
Lifecycle method class dan padanan hook-nya
Class component menuliskan lifecycle sebagai method bernama. Kamu akan menemuinya di kode lama dan di error boundary, yang masih membutuhkan class.
| Method class | Kapan berjalan | Padanan di function component |
|---|---|---|
constructor | sekali, sebelum render pertama | useState(initial), useRef |
static getDerivedStateFromProps | sebelum setiap render | hitung nilainya saat render, atau reset dengan key |
render | setiap render | isi fungsi |
componentDidMount | setelah render pertama masuk ke DOM | useEffect(fn, []) |
shouldComponentUpdate | sebelum update, bisa melewatkannya | React.memo (jawabannya kebalikan: "apakah props sama") |
getSnapshotBeforeUpdate | tepat sebelum DOM berubah | tidak ada hook langsung; useLayoutEffect dengan ref adalah yang paling mendekati |
componentDidUpdate | setelah setiap update | useEffect(fn, [deps]) (juga berjalan setelah mount) |
componentWillUnmount | sebelum dihapus | cleanup yang dikembalikan useEffect |
getDerivedStateFromError, componentDidCatch | saat anak melempar error | tidak ada: tulis class error boundary |
Berikut class yang mencatat setiap method saat berjalan:
Mounting mencatat constructor, getDerivedStateFromProps, render, componentDidMount. Sebuah klik mencatat getDerivedStateFromProps, shouldComponentUpdate, render, getSnapshotBeforeUpdate, componentDidUpdate. Buat shouldComponentUpdate mengembalikan false, dan klik tidak lagi memperbarui tombol: state berubah, tetapi React melewatkan render. Halaman class component menunjukkan cara mengubah class seperti ini menjadi fungsi.
Berpikir dalam sinkronisasi, bukan lifecycle
Tabel hook ini berguna untuk membaca kode lama, tetapi bisa membuatmu menulis effect yang meniru kebiasaan class, misalnya satu effect "did mount" besar dan satu effect "did update" besar. Effect lebih tepat dibaca sebagai "jaga agar sesuatu di luar ini tetap sinkron dengan nilai-nilai ini": sambungkan ke roomId, dan saat roomId berubah, putuskan lalu sambungkan lagi. Tulis satu effect untuk setiap hal yang kamu sinkronkan, cantumkan nilai yang dipakainya sebagai dependensi, dan biarkan React yang menentukan kapan menjalankannya.
Pertanyaan yang sering diajukan
Apa saja fase lifecycle komponen React?
Mounting (React membuat komponen dan menampilkan output-nya di layar), updating (komponen dirender lagi karena state, props, atau context-nya berubah), dan unmounting (komponen dihapus dan membersihkan dirinya).
Apa padanan hook untuk componentDidMount?
useEffect(() => { ... }, []). Array dependensi yang kosong membuatnya berjalan sekali, setelah render pertama tampil di layar. Fungsi yang dikembalikannya adalah padanan componentWillUnmount.
Apa padanan hook untuk componentDidUpdate?
useEffect dengan nilai yang kamu pedulikan di array dependensi, misalnya [userId]. Berbeda dengan componentDidUpdate, effect ini juga berjalan setelah render pertama, jadi mencakup mount dan update sekaligus.
Dalam urutan apa effect induk dan anak berjalan?
React merender dari induk ke bawah, tetapi menjalankan effect dari anak ke atas: effect milik anak berjalan sebelum effect induknya. Saat unmount, cleanup induk berjalan sebelum cleanup anak.
Apa itu fase render dan fase commit?
Di fase render, React memanggil komponenmu untuk menentukan seperti apa tampilan layar seharusnya; pekerjaan ini bisa dijeda atau dibuang. Di fase commit, React menerapkan perubahan ke DOM dan menjalankan layout effect; effect biasa berjalan setelahnya, biasanya setelah browser selesai menggambar.