useSyncExternalStore memungkinkan sebuah komponen membaca data yang berada di luar React tanpa risiko gangguan visual yang dapat ditimbulkan oleh concurrent rendering. Hook ini menggantikan pola lama yaitu menarik nilai eksternal ke dalam local state menggunakan useEffect, sebuah pola yang dapat menyebabkan “tearing” – di mana bagian-bagian UI menampilkan nilai yang berbeda selama render yang sama.
Mengapa pola lama bermasalah dalam mode concurrency
Sebelum React 18, resep umumnya adalah:
const [value, setValue] = useState(initial);
useEffect(() => {
const unsubscribe = externalSource.subscribe(v => setValue(v));
return unsubscribe;
}, []);
Effect berjalan setelah komponen selesai di-render. Ketika concurrent renderer milik React memutuskan untuk menjeda, membatalkan, atau mengulang render, effect tersebut mungkin berjalan pada titik yang berbeda dengan UI yang membaca state. Hasilnya adalah ketidakcocokan: satu komponen mungkin telah membaca nilai lama, sementara komponen lain membaca nilai baru, dan layar akan menampilkan data yang tidak konsisten untuk sesaat. “Tearing” ini tidak berbahaya dalam dunia yang sepenuhnya sinkron, tetapi menjadi bug yang terlihat ketika React merender beberapa versi UI secara paralel.
Bagaimana useSyncExternalStore menyelesaikan masalah tersebut
Hook ini memerlukan dua callback:
- subscribe – mendaftarkan listener dan mengembalikan fungsi untuk menghapusnya.
- getSnapshot – mengembalikan nilai saat ini dari sumber eksternal.
React memanggil subscribe saat komponen di-mount dan menghapusnya (dispose) saat unmount. Selama setiap render, React memanggil getSnapshot dan menjamin bahwa semua komponen yang menggunakan external store yang sama membaca snapshot yang persis sama. Karena pembacaan terjadi di dalam fase render, renderer dapat mengoordinasikan pembaruan di seluruh tree, sehingga menghilangkan tearing.
Library seperti Zustand dan Redux sudah membungkus store mereka dengan hook ini, itulah sebabnya mereka dapat langsung digunakan dengan fitur concurrent React 18.
Aturan agar hook tidak berperilaku salah
- Kembalikan hanya apa yang dibutuhkan komponen – objek yang besar memaksa React untuk membandingkan struktur yang dalam pada setiap pembaruan. Snapshot yang sempit menjaga deteksi perubahan tetap ringan.
- Pertahankan identitas referensi – jika nilai eksternal tidak berubah, getSnapshot harus mengembalikan referensi objek yang sama. Mengembalikan objek baru pada setiap panggilan akan memicu loop render tak terbatas karena React melihat adanya perubahan pada setiap render.
- Batasi penggunaan hanya untuk sumber eksternal – local component state seharusnya berada di dalam
useStateatauuseReducer. Menggunakan hook external-store untuk nilai yang murni internal hanya akan menambah indirection yang tidak perlu.
Kapan harus menggunakan useSyncExternalStore
- Membungkus Browser API (window size, network-online status, media queries).
- Menghubungkan JavaScript store biasa yang tidak mengenal React.
- Mengganti prop-drilling dengan sumber read-only global yang diikuti (subscribe) oleh banyak komponen.
Argumen tandingan
Pola useState + useEffect masih berfungsi untuk data sederhana yang jarang berubah, terutama ketika aplikasi tidak mengaktifkan fitur concurrent. Ini adalah pendekatan tingkat rendah yang menurut beberapa pengembang lebih mudah dipahami karena logika subscription berada secara eksplisit di dalam effect. Namun, risikonya adalah potensi tearing setelah concurrent rendering diaktifkan. Untuk proyek yang sudah menggunakan kemampuan baru React 18, keamanan tambahan dari useSyncExternalStore lebih berharga daripada sedikit tambahan boilerplate.
Hal yang perlu diperhatikan selanjutnya
- Pastikan getSnapshot pada store Anda mengembalikan referensi yang stabil; melakukan
console.logcepat pada referensi tersebut selama pengembangan dapat mengungkap pembuatan ulang objek yang tidak disengaja. - Jaga agar fungsi subscription tetap ringan – pekerjaan berat harus dilakukan di dalam sumber eksternal, bukan di dalam callback yang diteruskan ke React.
- Uji komponen di bawah mode concurrent React (misalnya, menggunakan
actdenganReactDOM.createRoot) untuk memastikan tidak ada tearing yang muncul.
Demo langsung menunjukkan cara kerja hook ini, menyinkronkan penghitung (counter) sederhana yang disimpan di luar React dan memperbarui beberapa komponen tanpa gangguan visual. Demo tersebut tersedia di https://usesyncexternalstore.vercel.app/. Kode sumbernya tersedia di GitHub, dan tulisan yang lebih panjang menjelaskan detail implementasinya.
Kesimpulan: Di dunia di mana React dapat merender bagian-bagian UI secara tidak berurutan, useSyncExternalStore adalah jembatan yang andal antara data eksternal dan perenderan komponen. Hook ini menjaga konsistensi visual, dapat diskalakan ke global store, dan sudah terintegrasi ke dalam library manajemen state populer. Gunakanlah di mana pun Anda perlu membaca state non-React, dan pastikan snapshot tetap kecil serta stabil untuk mendapatkan manfaat maksimalnya.
