React 18 menambahkan useSyncExternalStore, sebuah hook yang memungkinkan komponen membaca data dari sumber apa pun yang berada di luar tree React—API browser, WebSocket, atau store pihak ketiga—tanpa gangguan visual (glitch) yang sering terjadi pada pola lama saat mencerminkan data tersebut dengan useState dan useEffect. Jika Anda pernah melihat UI berkedip (flicker) karena sebagian menampilkannya ukuran jendela yang lama sementara bagian lain sudah mencerminkan ukuran yang baru, hook ini adalah solusi yang Anda tunggu-tunggu.
Mengapa state eksternal membutuhkan hook baru
Komponen React selalu dapat berlangganan (subscribe) ke hal-hal yang ada di luar cakupan mereka sendiri: objek window, store Redux, atau koneksi WebSocket. Pendekatan konvensionalnya adalah dengan menyiapkan langganan di dalam useEffect, memasukkan nilai yang masuk ke dalam local state melalui setState, dan membiarkan React melakukan re-render. Hal ini berjalan baik saat React merender secara sinkron, tetapi React 18 memperkenalkan concurrent rendering, di mana sebuah komponen dapat dirender berkali-kali sebelum browser benar-benar melakukan paint. Dalam mode tersebut, pola “mirror-into-local-state” dapat menyebabkan tearing—bagian UI yang berbeda membaca snapshot yang berbeda dari nilai eksternal yang sama dalam satu siklus render.
useSyncExternalStore dibuat untuk mencegah tearing. Hook ini meminta snapshot (nilai saat ini) dari sumber eksternal dan sebuah fungsi subscribe (cara untuk mendapatkan notifikasi perubahan). React memanggil subscribe saat komponen di-mount dan secara otomatis melakukan unsubscribe saat unmount. Setiap kali snapshot yang dikembalikan oleh getSnapshot berubah, React akan menjadwalkan render yang membaca snapshot yang sama untuk seluruh tree komponen, sehingga menjamin konsistensi.
Dua fungsi yang diperlukan
| Fungsi | Kegunaannya |
|---|---|
| subscribe | Menerima callback yang harus dipanggil setiap kali nilai eksternal berubah. Fungsi ini mengembalikan fungsi unsubscribe yang akan dipanggil React saat pembersihan (cleanup). |
| getSnapshot | Mengembalikan nilai saat ini dari sumber eksternal. Fungsi ini harus referentially stable—objek fungsi yang sama pada setiap render—agar React dapat membandingkan snapshot dengan benar. |
Implementasi minimal untuk status online browser terlihat seperti ini:
function useOnlineStatus() {
return useSyncExternalStore(
(onChange) => {
window.addEventListener('online', onChange);
window.addEventListener('offline', onChange);
return () => {
window.removeEventListener('online', onChange);
window.removeEventListener('offline', onChange);
};
},
() => navigator.onLine
);
}
Saat komponen apa pun memanggil useOnlineStatus(), React hanya akan melakukan re-render ketika navigator.onLine benar-benar berubah, dan setiap render akan melihat nilai yang sama.
Aturan agar hook tidak bermasalah
Kembalikan potongan data terkecil yang dibutuhkan komponen.
Jika store eksternal menyimpan objek besar tetapi komponen hanya peduli pada satu field, kembalikan field tersebut saja. Mengembalikan data yang lebih besar akan menyebabkan re-render yang tidak perlu.Memoisasi
getSnapshot.
Jika Anda membuat ulang fungsi tersebut pada setiap render, React akan menganggapnya sebagai sumber baru dan dapat menyebabkan loop selamanya. Bungkus denganuseCallbackatau definisikan di luar komponen.Hindari mengembalikan objek baru pada setiap pemanggilan.
Mengembalikan objek baru (misalnya,{ count: store.getCount() }) menciptakan referensi baru pada setiap render, yang membuat React mengira snapshot telah berubah dan memicu loop render tak terbatas. Gunakan tipe primitif, string, angka, atau objek yang telah dimemoisasi.Jangan gunakan untuk state internal komponen.
useStatetetap merupakan alat yang tepat untuk data yang hanya berada di dalam komponen.useSyncExternalStoremenambah overhead yang tidak diperlukan untuk local state.
Kapan harus menggunakannya
- API Browser – ukuran jendela, media queries,
navigator.onLine, status baterai. - Store yang agnostik terhadap framework – Zustand, Redux, MobX, atau store kustom apa pun yang mengekspos API langganan (subscription API).
- Sumber mutable yang diperbarui di luar React – pesan WebSocket, event perubahan IndexedDB, notifikasi Service Worker.
Jika sumber data Anda sudah berada di dalam React (misalnya, state dari komponen induk), tetaplah gunakan useState atau context.
Apa yang dikatakan komunitas
Pengguna awal melaporkan bahwa useSyncExternalStore menghilangkan kedipan (flicker) yang mereka lihat saat mengubah ukuran jendela dalam pengaturan concurrent-rendering. Beberapa library telah beralih menggunakan API ini untuk hook internal mereka, menjanjikan perilaku yang lebih terprediksi di berbagai versi React. Kompensasinya adalah beban kognitif yang sedikit lebih tinggi: pengembang harus memikirkan stabilitas dari kedua fungsi tersebut dan menghindari pengembalian objek baru pada setiap pemanggilan.
Apa yang perlu diperhatikan selanjutnya
Rilis React mendatang mungkin akan memperketat kontrak di sekitar useSyncExternalStore, kemungkinan dengan menambahkan helper bawaan untuk API browser yang umum. Pantau terus blog resmi React untuk melihat apakah ada depresiasi pada pola lama yang meniru useEffect. Sementara itu, hook ini sudah stabil dan merupakan bagian dari API publik, sehingga Anda dapat dengan aman melakukan refaktor pada kode state eksternal yang sudah ada.
Uji coba cepat
Demo langsung yang menampilkan sinkronisasi ukuran jendela, deteksi online/offline, dan store Zustand sederhana tersedia di https://usesyncexternalstore.vercel.app/. Kode sumbernya, lengkap dengan komentar, tersedia di https://github.com/dev48v/usesyncexternalstore.
Kesimpulan: useSyncExternalStore memberikan React jembatan yang andal ke data mutable apa pun yang berada di luar siklus render-nya, mencegah tearing dan render yang tidak perlu. Terapkan pada sumber eksternal, jaga agar snapshot tetap minimal dan stabil, dan biarkan React menangani tugas berat dalam menjaga konsistensi.
