Bagaimana Shortcut Global Menyebabkan Hydration Mismatch
Hydration mismatch terjadi ketika DOM hasil SSR berbeda dari DOM yang dibuat ulang saat JavaScript klien dijalankan. Jika shortcut global (misalnya kombinasi keyboard untuk mengubah tema atau navigasi) men-trigger perubahan sebelum browser selesai melakukan hydration, maka versi klien bisa mengubah struktur atau atribut DOM yang sudah dipasang server. Jawabannya adalah memperlambat interaksi shortcut sampai framework selesai menyerahkan kontrol ke klien, sambil memastikan event hanya mengubah bagian DOM yang tidak memengaruhi markup hasil SSR.
Menurut Are We GlobalShortcuts Yet?, shortcut global memiliki akses sebelum komponen klien siap karena mereka melekat di atas level aplikasi. Dalam konteks SSR, itu berarti pembaca shortcut potensial bisa menimbulkan perbedaan antara HTML statis yang dikirim server dan DOM yang akan dihias klien.
Memantau Diff DOM untuk Menangkap Mismatch
Sebelum mengambil keputusan, deteksi apakah shortcut telah mengubah DOM yang diharapkan. Gunakan MutationObserver untuk memantau atribut atau node yang kritikal, lalu bandingkan dengan snapshot awal.
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
if (mutation.type === 'attributes' && mutation.target.matches('[data-ssr-protected]')) {
console.warn('Shortcut merubah DOM SSR:', mutation.target);
}
}
});
observer.observe(document.documentElement, { attributes: true, subtree: true });
Target dengan atribut data-ssr-protected merupakan contoh markup yang tidak boleh diubah sampai hydration tuntas. Dengan log ini, Anda bisa mengidentifikasi elemen mana yang terpukul dan menyesuaikan shortcut agar hanya memodifikasi bagian klien.
Menunda Attachment Event Hingga Hydration Selesai
Alih-alih langsung menambahkan listener global di document atau window, tunggu hingga indikator hydration selesai. Misalnya, framework dapat menandai status setelah root selesai di-hydrate:
function initializeGlobalShortcuts() {
const handleShortcut = (event) => {
if (!window.__HYDRATION_COMPLETE__) return;
if (event.key === 'k' && event.metaKey) {
toggleCommandPalette();
}
};
document.addEventListener('keydown', handleShortcut);
}
if (document.readyState === 'complete') {
window.__HYDRATION_COMPLETE__ = true;
initializeGlobalShortcuts();
} else {
window.addEventListener('load', () => {
window.__HYDRATION_COMPLETE__ = true;
initializeGlobalShortcuts();
});
}
Menandai window.__HYDRATION_COMPLETE__ memastikan listener tidak bereaksi sebelum framework klien siap. Jika framework Anda menyediakan hook lifecycle resmi (misalnya useHydrated atau event custom), gunakan untuk mengganti penanda manual.
Mengisolasi State Shortcut dari SSR
State yang dikelola shortcut harus terpisah dari state SSR agar perubahan tidak menyentuh DOM server-first. Simpan state lokal di luar tree kawalan SSR.
- Gunakan portal atau layer overlay untuk menampung UI shortcut (seperti modal command palette) sehingga Anda tidak mengubah elemen SSR.
- Sinkronkan state awal dengan markup SSR melalui atribut data (contoh:
data-theme="light"), lalu perbarui attr tersebut hanya setelah hydration. - Hindari mengganti struktur DOM penting seperti layout grid yang ditentukan server; gunakan transform/opacity kecil untuk memberi persepsi perubahan.
Dengan cara ini, shortcut hanya beroperasi di layer client yang tidak memengaruhi HTML dasar dari server.
Debugging Praktis dan Checklist Aksesibilitas
Gunakan pendekatan sistematis untuk memastikan shortcut tidak menyebabkan mismatch:
- Reproduksi di mode development: buka devtools, refresh dengan fitur “Disable cache” dan lihat pesan mismatch di konsol.
- Bandingkan snapshot DOM: ambil HTML dari response SSR (misalnya lewat
curl) lalu bandingkan dengan DOM klien (perintahdocument.documentElement.outerHTML) untuk melihat diff yang disebabkan shortcut. - Gunakan logging Event: log event shortcut yang dipicu segera setelah hydration selesai untuk memastikan tidak terjadi sebelum waktu yang tepat.
- Periksa state server: pastikan data dari server tidak di-rewrite tanpa validasi; misalnya, jika server mengirimkan tema default, shortcut harus menunggu dulu atau memicu fallback.
Untuk aksesibilitas, tambahkan checklist singkat:
- Shortcut dapat dinonaktifkan melalui preferensi pengguna.
- Fokus keyboard tetap terjaga—tidak lompat ke elemen tersembunyi.
- Label visual untuk state shortcut sinkron dengan atribut
aria-pressedatauaria-expanded. - Perilaku tetap konsisten pada device assistive technology (ujicoba dengan pembaca layar).
Contoh Fallback Saat Shortcut Menimpa State Server
Misalnya server menyajikan tema berdasarkan preferensi pengguna, namun shortcut global mengubah tema lebih awal. Anda bisa mengombinasikan state server dan client dengan strategi fallback:
const serverTheme = document.documentElement.getAttribute('data-theme') ?? 'light';
let clientTheme = serverTheme;
function applyTheme(theme) {
clientTheme = theme;
document.documentElement.setAttribute('data-theme', theme);
}
function handleThemeShortcut(event) {
if (!window.__HYDRATION_COMPLETE__) return;
if (event.key === 't' && event.metaKey) {
applyTheme(clientTheme === 'light' ? 'dark' : 'light');
}
}
document.addEventListener('keydown', handleThemeShortcut);
window.addEventListener('hydration-ready', () => {
window.__HYDRATION_COMPLETE__ = true;
applyTheme(serverTheme);
});
Ketika hydration selesai, event handler memulai dari serverTheme. Jika shortcut menimpa state sebelum event hydration-ready, listener akan mengabaikannya dan fallback ke tema server saat ready.
Kesimpulan
Solusi utama untuk menghindari hydration mismatch akibat shortcut global adalah memastikan event tidak aktif sebelum hydration selesai, memantau diff DOM pada area kritis, dan menjaga state shortcut terisolasi dari markup SSR. Dengan checklist debugging dan aksesibilitas, Anda dapat memverifikasi bahwa shortcut tetap responsif tanpa merusak pengalaman SSR. Strategi fallback memastikan server tetap memegang kendali awal sampai klien siap.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!