useSyncExternalStore membolehkan komponen membaca data yang berada di luar React tanpa risiko gangguan visual yang boleh diperkenalkan oleh rendering serentak (concurrent rendering). Ia menggantikan corak lama iaitu menarik nilai luaran ke dalam state tempatan menggunakan useEffect, satu corak yang boleh menyebabkan "tearing" – bahagian UI menunjukkan nilai yang berbeza semasa render yang sama.

Mengapa corak lama gagal di bawah concurrency

Sebelum React 18, resipi biasa adalah:

const [value, setValue] = useState(initial);
useEffect(() => {
  const unsubscribe = externalSource.subscribe(v => setValue(v));
  return unsubscribe;
}, []);

Effect berjalan selepas komponen telah dirender. Apabila renderer serentak React memutuskan untuk menjeda, membatalkan, atau memainkan semula render, effect mungkin dicetuskan pada titik yang berbeza daripada UI yang membaca state tersebut. Hasilnya ialah ketidakpadanan: satu komponen mungkin telah membaca nilai lama, manakala yang lain membaca nilai baharu, dan skrin menunjukkan data yang tidak konsisten buat seketika. "Tearing" ini tidak berbahaya dalam dunia yang bersifat sinkronus sepenuhnya, tetapi menjadi pepijat (bug) yang nyata apabila React merender pelbagai versi UI secara selari.

Bagaimana useSyncExternalStore menyelesaikan masalah

Hook ini memerlukan dua callback:

  • subscribe – mendaftarkan pendengar (listener) dan mengembalikan fungsi untuk membuangnya.
  • getSnapshot – mengembalikan nilai semasa sumber luaran.

React memanggil subscribe apabila komponen di-mount dan membuangnya (dispose) apabila di-unmount. Semasa setiap render, ia memanggil getSnapshot dan menjamin bahawa semua komponen yang menggunakan stor luaran yang sama membaca snapshot yang tepat sama. Oleh kerana pembacaan berlaku di dalam fasa render, renderer boleh menyelaraskan kemas kini merentasi keseluruhan pokok (tree), sekali gus menghapuskan tearing.

Library seperti Zustand dan Redux sudah pun membungkus stor mereka dengan hook ini, sebab itulah ia berfungsi secara terus dengan ciri serentak React 18.

Peraturan untuk mengelakkan hook daripada bermasalah

  1. Kembalikan hanya apa yang diperlukan oleh komponen – objek yang besar memaksa React untuk membandingkan struktur yang mendalam pada setiap kemas kini. Snapshot yang sempit memastikan pengesanan perubahan kekal murah (efficient).
  2. Kekalkan identiti rujukan (referential identity) – jika nilai luaran tidak berubah, getSnapshot mesti mengembalikan rujukan objek yang sama. Mengembalikan objek baharu pada setiap panggilan akan mencetuskan gelung render infiniti kerana React melihat perubahan pada setiap render.
  3. Hadkan penggunaan kepada sumber luaran sahaja – state komponen tempatan sepatutnya berada dalam useState atau useReducer. Menggunakan hook stor luaran untuk nilai dalaman semata-mata menambah lapisan tidak langsung (indirection) yang tidak perlu.

Bila perlu menggunakan useSyncExternalStore

  • Membungkus API pelayar (saiz window, status rangkaian-online, media queries).
  • Menyambungkan stor JavaScript biasa yang tidak mengetahui tentang React.
  • Menggantikan prop-drilling dengan sumber bacaan global (read-only) yang dilanggan oleh banyak komponen.

Hujah balas

Corak useState + useEffect masih berfungsi untuk data yang ringkas dan jarang berubah, terutamanya apabila aplikasi tidak mengaktifkan ciri serentak. Ia adalah pendekatan tahap rendah yang dianggap lebih mudah difahami oleh sesetengah pembangun kerana logik langganan (subscription) berada secara eksplisit dalam effect. Walau bagaimanapun, risikonya ialah kemungkinan berlakunya tearing sebaik sahaja rendering serentak diaktifkan. Untuk projek yang sudah menggunakan keupayaan baharu React 18, keselamatan tambahan daripada useSyncExternalStore adalah lebih berbaloi berbanding sedikit penambahan boilerplate.

Apa yang perlu diperhatikan seterusnya

  • Pastikan getSnapshot stor anda mengembalikan rujukan yang stabil; console.log pantas pada rujukan tersebut semasa pembangunan boleh mendedahkan penciptaan semula objek yang tidak disengajakan.
  • Pastikan fungsi langganan (subscription) adalah ringan – kerja yang berat harus berlaku di dalam sumber luaran, bukan dalam callback yang dihantar ke React.
  • Uji komponen di bawah mod serentak React (contohnya, menggunakan act dengan ReactDOM.createRoot) untuk mengesahkan bahawa tiada tearing berlaku.

Demo langsung menunjukkan hook ini berfungsi, menyelaraskan pengira (counter) ringkas yang disimpan di luar React dan mengemas kini pelbagai komponen tanpa gangguan visual. Demo tersebut tersedia di https://usesyncexternalstore.vercel.app/. Kod sumber berada di GitHub, dan penulisan yang lebih panjang menjelaskan butiran pelaksanaan.

Rumusan: Dalam dunia di mana React boleh merender bahagian UI secara tidak mengikut urutan, useSyncExternalStore adalah jambatan yang boleh dipercayai antara data luaran dan rendering komponen. Ia mengekalkan konsistensi visual, boleh diskalakan ke stor global, dan sudah tersedia dalam library pengurusan state yang popular. Gunakannya di mana-mana sahaja anda perlu membaca state bukan-React, dan pastikan snapshot kekal kecil serta stabil untuk mendapat manfaat sepenuhnya.