useTransition membuatmu bisa menandai pembaruan state sebagai tidak mendesak. React merender pembaruan itu di latar belakang dan membuang atau memulainya ulang kalau terjadi sesuatu yang lebih mendesak, seperti klik atau ketukan tombol, sehingga halaman tetap responsif selagi bagian layar yang lambat menyusul.
Klik posts lalu, sebelum daftarnya muncul, klik contact. Contact langsung tampil: React membuang render posts yang setengah jadi. Sekarang ganti startTransition(() => { setTab(next); }) menjadi setTab(next); biasa lalu coba hal yang sama: halaman membeku sekitar 300 ms setelah mengklik posts, dan klikmu pada contact harus menunggu sampai posts selesai.
Sintaksnya
const [isPending, startTransition] = useTransition();
startTransition(() => {
setTab(next);
});
startTransitionmenerima sebuah fungsi dan langsung menjalankannya. Setiap pembaruan state di dalamnya ditandai sebagai transition.isPendingbernilaitruesejak kamu memulai transition sampai React selesai merendernya dan meng-commit hasilnya.
useTransition tidak menerima argumen. Fungsi yang kamu berikan berjalan secara sinkron; transition berkaitan dengan cara React merender pembaruan yang dihasilkan, bukan dengan kapan fungsimu berjalan.
Arti "tidak mendesak"
React membagi pembaruan menjadi dua jenis. Pembaruan mendesak, seperti mengetik, mengklik, dan menekan, harus langsung terlihat, kalau tidak halaman terasa rusak. Pembaruan transition, seperti berpindah ke tampilan baru atau menampilkan hasil yang difilter, boleh butuh waktu sejenak.
React merender transition dalam potongan-potongan kecil dan memeriksa di antara komponen apakah ada sesuatu yang mendesak masuk. Kalau ada, React menjeda atau meninggalkan transition itu, menangani pembaruan yang mendesak, lalu memulai transition lagi dengan state terbaru. Selama transition dirender, layar sebelumnya tetap tampil; React tidak menampilkan hasil yang setengah jadi. Itulah sebabnya contoh tadi terus menampilkan teks about, dengan petunjuk Loading di samping tombol, sampai posts siap.
Penyelaan terjadi di antara komponen. Satu komponen yang memblokir selama 300 ms tidak bisa dipotong di tengah, itulah sebabnya pekerjaan lambat di atas dipecah menjadi 300 komponen SlowPost yang kecil.
Menampilkan petunjuk pending
isPending adalah tempat untuk indikator loading yang tidak menggantikan konten saat ini: spinner di samping tombol, teks yang diredupkan, label "Saving...". Di contoh pertama, Loading... muncul di samping tab. Kamu juga bisa memakainya untuk memberi style pada konten:
<div style={{ opacity: isPending ? 0.6 : 1 }}>{content}</div>
Memfilter daftar besar selagi mengetik
Kamu tidak bisa menaruh nilai input itu sendiri di dalam transition. <input> yang controlled harus menerima nilai barunya dalam pembaruan mendesak yang sama, kalau tidak karakter akan hilang. Simpan dua state sebagai gantinya: teks input (mendesak) dan query yang dipakai daftar (transition).
Ketik 123 dengan cepat. Input-nya tidak pernah tersendat. Console tetap mencatat satu baris rendering results per ketukan tombol, karena Results mencatat log begitu React mulai merendernya, tapi daftar untuk 1 dan 12 tidak pernah sampai ke layar: setiap ketukan yang lebih baru menyela render yang sedang berjalan, dan React memulai lagi dengan query terbaru. Results sengaja dibungkus memo: tanpanya, render mendesak untuk setiap ketukan juga akan merender daftar yang lambat dengan query lama, dan kamu kehilangan manfaatnya.
startTransition tanpa hook
react juga meng-export startTransition sebagai fungsi biasa. Fungsi ini menandai pembaruan sebagai transition persis seperti hook-nya, tapi kamu tidak mendapat isPending. Pakai di tempat kamu tidak bisa memanggil hook, misalnya di library data atau router di luar komponen mana pun.
import { startTransition } from 'react';
function navigate(url) {
startTransition(() => {
setCurrentUrl(url);
});
}
Transition async di React 19
Sejak React 19, fungsi yang kamu berikan boleh berupa async. isPending lalu tetap true selama fungsi itu berjalan, termasuk saat menunggu, sehingga menjadi cara sederhana untuk menampilkan state pending selama request. saveName palsu di bawah menggantikan request sungguhan.
Perhatikan startTransition kedua di sekitar setSaved. React kehilangan jejak transition setelah melewati await, jadi pembaruan state sesudahnya harus dibungkus lagi agar terhitung sebagai bagian dari transition. React menyebut fungsi yang diberikan dengan cara ini sebagai action; useActionState dan form action dibangun di atas mekanisme yang sama.
Transition bukan debounce
Debounce menunggu waktu tetap setelah ketukan tombol terakhir sebelum melakukan apa pun. Transition tidak menunggu. Transition langsung mulai merender, dan kalau ada pembaruan yang lebih baru, pekerjaan lama dibuang. Di perangkat yang cepat, hasilnya muncul tanpa jeda sama sekali; di perangkat yang lambat, React mengerjakan sebanyak yang bisa di antara ketukan tombol. Yang tidak bisa dilakukan transition adalah mengurangi jumlah request jaringan: kalau setiap ketukan memulai fetch, lakukan debounce pada fetch-nya.
Transition dan Suspense
Transition juga mengubah apa yang dilakukan boundary Suspense. Biasanya, saat komponen di dalam boundary ditangguhkan (komponen lazy yang sedang dimuat, atau data yang dibaca dengan use), React mengganti kontennya dengan fallback. Kalau pembaruan yang menyebabkan penangguhan itu adalah transition, React tetap mempertahankan konten yang sudah ada di layar, dan isPending tetap true sampai konten baru siap. Beginilah cara router menghindari spinner seukuran halaman yang berkedip di setiap navigasi: router membungkus navigasi dalam transition, dan halaman lama tetap terlihat sampai halaman berikutnya bisa dirender. Konten yang belum pernah ditampilkan tetap memakai fallback, jadi boundary yang baru dimunculkan berperilaku seperti biasa.
Hal yang tidak berfungsi
- Pembaruan di dalam timeout.
startTransition(() => setTimeout(() => setTab(next), 100))tidak menandai pembaruannya, karena pembaruan itu berjalan setelahstartTransitionselesai. TaruhstartTransitiondi dalam timeout-nya. - Satu komponen yang lambat. React hanya bisa menyela di antara komponen, jadi satu komponen yang memblokir lama tetap memblokir.
- Mengendalikan input. Seperti di atas, state milik input itu sendiri tetap mendesak.
Untuk kasus saat kamu tidak memegang setter state dan hanya menerima sebuah nilai, seperti prop, useDeferredValue memberimu perilaku yang sama.
Pertanyaan yang sering diajukan
Apa fungsi useTransition?
useTransition memberimu startTransition, yang menandai pembaruan state di dalamnya sebagai tidak mendesak, dan isPending, yang bernilai true selama pembaruan itu masih dirender. React bisa menyela render yang tidak mendesak untuk menangani klik dan ketikan lebih dulu.
Apa bedanya useTransition dan startTransition?
Keduanya menandai pembaruan sebagai transition. Hook useTransition juga memberimu isPending untuk petunjuk loading. startTransition versi mandiri yang di-import dari react berfungsi di luar komponen tapi tidak punya flag pending.
Bisakah useTransition dipakai untuk input teks?
Tidak untuk nilai input itu sendiri. Controlled input harus diperbarui secara sinkron, kalau tidak ketikan akan rusak. Biarkan state input tetap mendesak dan taruh pembaruan berat yang bergantung padanya di dalam transition, atau pakai useDeferredValue.
Apakah useTransition sama dengan debounce?
Tidak. Debounce menunggu waktu tetap sebelum melakukan apa pun. Transition langsung mulai merender dan disela kalau ada pembaruan yang lebih baru, jadi di perangkat yang cepat hasilnya muncul tanpa jeda.
Bisakah startTransition menerima fungsi async?
Bisa, sejak React 19. isPending tetap true sampai fungsi async itu selesai. Pembaruan state setelah await harus dibungkus pemanggilan startTransition yang lain agar ditandai sebagai transition.