useDeferredValue memberimu salinan sebuah nilai yang boleh tertinggal. Saat nilainya berubah, React lebih dulu menggambar ulang layar dengan salinan yang lama, sehingga mengetik terasa instan, lalu merender bagian yang lambat dengan nilai baru di latar belakang.
Ketik sebuah kata dengan cepat. Input-nya tetap mengikuti dan daftarnya meredup selama masih tertinggal. Console mencatat satu baris SlowList rendering per huruf, karena SlowList mencatat log begitu React mulai merendernya, tapi daftar untuk nilai-nilai di antaranya tidak pernah sampai ke layar: setiap huruf baru menyela render latar belakang, dan React memulai ulang dengan teks terbaru. Ganti query={deferredQuery} menjadi query={query} lalu ketik lagi: sekarang setiap ketukan tombol harus menunggu seperempat detik untuk daftarnya.
Sintaksnya
const deferredValue = useDeferredValue(value);
const deferredValue = useDeferredValue(value, initialValue); // React 19
valueadalah apa pun yang kamu render: string dari input, angka, object. Nilai primitif bekerja paling baik, karena object baru di setiap render selalu dihitung sebagai perubahan.- Nilai yang dikembalikan sebagian besar waktu sama dengan
value. Tepat setelahvalueberubah, nilai itu masih menyimpan nilai sebelumnya selama satu render, lalu menyusul di render latar belakang.
Cara kerjanya
Setiap perubahan menghasilkan dua render:
- Render mendesak dengan
valueyang baru dandeferredValueyang lama. Input menampilkan teks baru. Komponen yang hanya menerimadeferredValuemendapat props yang sama seperti sebelumnya. - Render latar belakang dengan
deferredValueyang baru. Inilah render yang lambat. React merendernya sepotong demi sepotong dan meninggalkannya kalauvalueberubah lagi sebelum selesai, lalu memulai ulang dengan nilai terbaru.
Kamu bisa melihatnya terjadi dengan mencatat kedua nilai itu:
Ketik satu huruf, misalnya a, dan Console mencetak dua baris lagi: pertama text = "a" dengan deferred = "", lalu keduanya "a". Render pertama itulah yang menjaga agar mengetik tetap cepat, tapi hanya kalau bagian yang lambat bisa melewatinya. Itulah sebabnya SlowList di contoh pertama dibungkus memo: selama render mendesak, prop query miliknya belum berubah, jadi React melewatinya. Tanpa memo, render mendesak tetap menjalankan seluruh daftar yang lambat, hanya dengan teks lama, dan kamu tidak mendapat apa-apa.
Menampilkan konten yang sudah usang
Selama render latar belakang masih berjalan, value dan nilai deferred berbeda. Bandingkan keduanya untuk memberi tahu pengguna bahwa kontennya sudah tidak terbaru, tanpa menyembunyikannya:
const isStale = query !== deferredQuery;
<div style={{ opacity: isStale ? 0.5 : 1, transition: 'opacity 0.2s' }}>
<SlowList query={deferredQuery} />
</div>
Ini biasanya lebih nyaman daripada spinner: hasil lama tetap terbaca dan berubah di tempat saat hasil baru siap. Ide yang sama berlaku dengan Suspense: kalau nilai deferred membuat sebuah komponen ditangguhkan selama memuat data, React tetap menampilkan konten lama alih-alih fallback sampai konten baru siap (lihat Suspense dan lazy).
Nilai awal (React 19)
Secara default, render pertama tidak punya apa pun untuk ditinggalkan, jadi nilai deferred sudah sama dengan nilai asli sejak awal. Sejak React 19, kamu bisa memberikan argumen kedua. Di render pertama, hook mengembalikan initialValue, lalu React merender lagi di latar belakang dengan nilai yang sebenarnya. Ini membuatmu bisa menampilkan sesuatu yang ringan seketika dan mengisi bagian yang berat tepat sesudahnya.
Console menampilkan dua render: pertama dengan "", lalu dengan teks lengkapnya. Hapus argumen kedua dan hanya satu render yang tercatat, langsung dengan teks lengkap.
useDeferredValue vs useTransition
Keduanya menandai pekerjaan sebagai tidak mendesak dan memakai mesin rendering yang sama. Perbedaannya ada di tempat kamu memakainya:
- useTransition membungkus pembaruannya. Kamu memanggil
startTransition(() => setQuery(text)), jadi kamu butuh setter-nya. Kamu mendapatisPendingsecara cuma-cuma. - useDeferredValue membungkus nilainya. Kamu memanggilnya di tempat nilai itu dipakai, jadi hook ini berfungsi saat nilainya datang sebagai prop atau berasal dari hook yang tidak kamu kendalikan. Kamu menghitung "pending" sendiri dengan
value !== deferredValue.
// You own the state: either works, a transition is direct
startTransition(() => setQuery(nextQuery));
// You only receive the value
function SearchResults({ query }) {
const deferredQuery = useDeferredValue(query);
return <SlowList query={deferredQuery} />;
}
useDeferredValue vs debounce dan throttle
Debounce menunggu waktu tetap setelah perubahan terakhir; throttle memperbarui paling banyak sekali per interval. Keduanya memilih jeda sejak awal, jadi di laptop yang cepat keduanya membuat halaman lebih lambat dari yang perlu, dan di ponsel yang lambat jedanya mungkin masih terlalu pendek.
useDeferredValue tidak punya jeda. Render latar belakang dimulai seketika, jadi di perangkat yang cepat daftarnya diperbarui hampir seketika, dan di perangkat yang lambat React menjaga input tetap responsif dan merender daftarnya sesering yang bisa dilakukannya. Pekerjaan latar belakang juga bisa disela, sesuatu yang tidak bisa dilakukan render dengan debounce: begitu render yang di-debounce dimulai, render itu memblokir sampai selesai.
Yang tidak dilakukannya adalah mengurangi request jaringan. Kalau setiap nilai baru memulai fetch, nilai deferred tetap berubah di setiap render yang selesai, jadi lakukan debounce pada request-nya sendiri saat jumlah request itu penting.
Kesalahan umum
- Lupa
memo. Seperti yang ditunjukkan di atas, komponen yang lambat harus bisa melewati render mendesak. Kalau komponen itu tidak dibungkusmemo, atau salah satu prop lainnya adalah object baru di setiap render, komponen itu dirender dua kali per ketukan tombol, bukan sekali. - Memberikan object baru.
useDeferredValue({ query })membuat object baru di setiap render, jadi nilai deferred selalu terlihat berubah dan React memulai render latar belakang tambahan setiap kali. Tunda nilai primitifnya (useDeferredValue(query)) lalu susun object dari nilai itu. - Berharap request berkurang. Komponen yang melakukan fetch berdasarkan nilai deferred tetap melakukan fetch untuk setiap nilai yang selesai dirender.
Kapan memakainya
Pakai useDeferredValue saat satu input menggerakkan bagian halaman yang lambat dirender, bagian lambat itu bisa di-memoize, dan kamu tidak keberatan menampilkan hasil yang sedikit usang sejenak: hasil pencarian, grafik yang digambar ulang dari sebuah filter, pratinjau teks berformat. Kalau bagian yang lambat itu lambat karena alasan yang bisa kamu perbaiki, seperti merender 5.000 baris padahal yang terlihat hanya 50, perbaiki itu dulu. Untuk versi pola ini saat kamu memegang setter-nya, lihat useTransition.
Pertanyaan yang sering diajukan
Apa fungsi useDeferredValue?
useDeferredValue mengembalikan versi sebuah nilai yang boleh tertinggal dari nilai aslinya. Saat nilainya berubah, React lebih dulu merender dengan nilai deferred yang lama, lalu merender lagi di latar belakang dengan nilai yang baru, dan render latar belakang itu bisa disela.
Mengapa useDeferredValue tidak membuat daftar saya lebih cepat?
Komponen yang lambat harus dibungkus memo dan menerima nilai deferred. Kalau tidak, render mendesak tetap merender komponen lambat itu, hanya dengan nilai lama, dan mengetik tetap terasa lambat.
Apa bedanya useDeferredValue dan useTransition?
useTransition membungkus pembaruan state, jadi kamu butuh akses ke setter-nya. useDeferredValue membungkus sebuah nilai, jadi hook ini berfungsi saat kamu hanya menerima nilainya, misalnya sebagai prop atau dari hook yang tidak kamu kendalikan.
Apakah useDeferredValue sama dengan debounce?
Tidak. Tidak ada jeda tetap. React memulai render deferred tepat setelah render mendesak, dan di perangkat yang cepat ketertinggalannya hampir tidak terlihat. Hook ini juga tidak mengurangi request jaringan dengan sendirinya.
Apa argumen kedua dari useDeferredValue?
Sejak React 19 kamu bisa memberikan initialValue. Di render pertama, hook mengembalikan nilai itu, lalu React merender lagi di latar belakang dengan nilai yang sebenarnya.