useLayoutEffect adalah versi useEffect yang berjalan lebih awal: tepat setelah React memperbarui DOM, tapi sebelum browser menggambar frame baru. Kode di dalamnya bisa mengukur elemen dan mengubah layout, dan pengguna hanya melihat hasil akhirnya. Hook ini memblokir proses menggambar selama berjalan, jadi pakai hanya saat kamu butuh waktu yang tepat seperti itu.
Effect-effect itu dideklarasikan dengan urutan effect, lalu layout effect, tapi console mencetak layout effect lebih dulu di setiap render. Urutan deklarasi tidak berpengaruh di antara kedua hook ini; yang berpengaruh adalah waktunya.
Sintaksnya
Signature-nya identik dengan useEffect: fungsi setup, cleanup opsional, dan array dependensi.
useLayoutEffect(() => {
// runs after the DOM update, before paint
return () => {
// cleanup, before the next run and on unmount
};
}, [dependencies]);
Semua yang kamu ketahui tentang dependensi dan cleanup dari useEffect tetap berlaku. Satu-satunya perbedaan adalah kapan React memanggilnya.
Kapan masing-masing berjalan
Setelah render, React memperbarui DOM lalu melakukan tiga hal dengan urutan tetap:
- Menjalankan semua cleanup layout effect dan layout effect, secara sinkron.
- Membiarkan browser menggambar layar. Kalau sebuah layout effect mengatur state, React merender lagi lebih dulu, tetap sebelum paint.
- Menjalankan effect biasa, biasanya setelah paint.
Di seluruh pohon, setiap layout effect berjalan sebelum effect biasa mana pun, dan child berjalan sebelum parent-nya, karena parent baru bisa selesai setelah child-nya berada di tempatnya:
Console menampilkan kedua layout effect lebih dulu, Child lalu Parent, kemudian kedua effect biasa dengan urutan yang sama. key={n} membuat setiap klik me-mount Parent yang baru, jadi kamu mendapat empat baris yang sama setiap kali. Tambahkan cleanup yang mencatat log ke setiap effect dan lihat yang lama berjalan sebelum yang baru.
Masalah kedipan
Bayangkan tooltip yang harus muncul di atas tombolnya, kecuali tidak ada ruang di atas, maka tooltip itu muncul di bawah. Kamu tidak bisa tahu apakah ada ruang sampai tooltip berada di DOM dan tingginya bisa diukur. Jadi rendering terjadi dalam dua tahap:
- Render tooltip di suatu tempat, agar bisa diukur.
- Ukur, hitung posisi sebenarnya, atur state, render lagi.
Dengan useEffect, browser mungkin menggambar layar di antara kedua tahap itu, dan pengguna melihat tooltip melompat dari posisi pertama ke posisi kedua. Dengan useLayoutEffect, tahap kedua terjadi sebelum paint, jadi posisi pertama tidak pernah ditampilkan.
Arahkan kursor ke tombol atas: tidak ada ruang di atasnya, jadi tooltip muncul di bawah. Arahkan ke tombol yang lebih bawah dan tooltip muncul di atas. Console memberi tahu sisi mana yang dipilih dari hasil pengukuran. Ganti useLayoutEffect menjadi useEffect lalu arahkan kursor lagi: tooltip sekarang bisa berkedip di bagian atas kotak selama satu frame sebelum berpindah. Di mesin yang cepat, kedipan itu sering terlalu cepat untuk disadari, dan justru itulah sebabnya bug ini sampai dirilis lalu dilaporkan dari ponsel yang lebih lambat.
offsetHeight, offsetTop, dan getBoundingClientRect() semuanya memaksa browser menghitung layout, jadi membacanya di layout effect memberimu nilai sebenarnya dari DOM yang baru saja ditulis React.
Mengukur sebuah elemen
Pola yang sama berlaku untuk ukuran apa pun yang menjadi tumpuan render: baca dengan ref di layout effect, simpan di state, lalu render dengan nilai itu.
Klik tombolnya dan baris lebar diperbarui dalam frame yang sama dengan teksnya. Halaman tidak pernah menampilkan teks baru di samping lebar lama, karena render kedua terjadi sebelum paint. Hapus [text] dari array dependensi dan pengukuran berjalan setelah setiap render, termasuk render yang dipicu oleh setWidth miliknya sendiri; pengukuran itu berhenti di situ hanya karena lebarnya tidak berubah lagi.
Utamakan useEffect
Layout effect menahan paint sampai selesai, dan kalau mengatur state, sampai render tambahannya juga selesai. Layout effect yang lambat membuat setiap pembaruan di halaman terasa lambat. Pakai useLayoutEffect hanya saat semua hal ini benar:
- Effect itu membaca layout (ukuran, posisi, scroll).
- Effect itu mengubah apa yang dirender berdasarkan bacaan tersebut.
- Keadaan perantaranya akan terlihat dan tampak salah.
Semua hal lain (fetch, subscription, timer, logging, analytics) tempatnya di useEffect. Aturan singkatnya: mulai dengan useEffect, dan ganti hanya kalau kamu bisa melihat kedipan.
useEffect biasanya berjalan setelah paint, tapi tidak selalu. Saat pembaruan datang dari aksi pengguna yang diskret seperti klik atau tekanan tombol, React mungkin menjalankan effect sebelum browser menggambar layar. Itulah salah satu alasan kedipan bisa muncul di satu situasi dan tidak di situasi lain.
Server rendering
Effect tidak pernah berjalan saat server rendering, begitu juga useLayoutEffect. Server mengirim HTML dari render pertama, dan layout effect baru berjalan setelah hidrasi di browser. React 18 dan versi sebelumnya memperingatkan "useLayoutEffect does nothing on the server" saat komponen seperti itu dirender di server; React 19 menghapus peringatan tersebut.
Kalau frame pertama hasil render server akan terlihat salah tanpa pengukuran, ada dua perbaikan yang umum: render fallback yang netral (misalnya, sembunyikan tooltip sampai selesai diukur), atau render komponennya hanya di client:
function ClientOnly({ children }) {
const [mounted, setMounted] = useState(false);
useEffect(() => setMounted(true), []);
return mounted ? children : null;
}
useInsertionEffect
Ada hook effect ketiga, useInsertionEffect, yang berjalan sebelum layout effect mana pun. Hook ini ada untuk library CSS-in-JS yang membuat tag <style> saat runtime: menyisipkan style di sana berarti layout effect yang mengukur sebuah elemen sudah melihatnya dalam keadaan diberi style. Hook ini tidak bisa memperbarui state dan ref belum terpasang, jadi tidak ada gunanya di kode aplikasi. Kalau kamu tidak sedang menulis library styling, kamu tidak akan membutuhkannya.
Pertanyaan yang sering diajukan
Apa bedanya useEffect dan useLayoutEffect?
Waktunya. useLayoutEffect berjalan secara sinkron setelah React mengubah DOM tapi sebelum browser menggambar layar, jadi pembaruan state-nya diterapkan sebelum pengguna melihat apa pun. useEffect biasanya berjalan setelah paint. API dan array dependensinya sama.
Kapan sebaiknya memakai useLayoutEffect?
Saat sebuah effect membaca layout (ukuran atau posisi) lalu langsung mengubah apa yang dirender berdasarkan bacaan itu, misalnya menempatkan tooltip di atas atau di bawah tombolnya. Tanpanya, pengguna bisa melihat posisi pertama yang salah selama satu frame.
Apakah useLayoutEffect buruk untuk performa?
Hook ini memblokir browser menggambar layar sampai selesai, bersama render ulang apa pun yang dipicunya. Layout effect yang lambat membuat seluruh halaman terasa lambat, jadi pakai useEffect kecuali kamu perlu menghindari kedipan yang terlihat.
Apakah useLayoutEffect berjalan di server?
Tidak. Kedua effect tidak berjalan saat server rendering. Versi React yang lebih lama mencetak peringatan saat komponen dengan useLayoutEffect dirender di server; React 19 tidak lagi melakukannya, tapi effect itu tetap hanya berjalan di browser.