Render mismatch dan perilaku UI membingungkan terjadi ketika state dinamis yang dibuat di server berbeda dari state yang dihidrat di klien. Di Rust Yew SSR, solusi langsung adalah memastikan data awal dan atribut/props tetap identik saat client mengambil alih, lalu menyediakan panduan diagnosa untuk menemukan penyebabnya.

Diagnosa Hydration yang Gagal

Langkah pertama adalah memahami kapan dan mengapa hydrat terjadi berbeda dari render server. Gunakan kombinasi:

  • Log hydratasi: Tambahkan log kondisi dalam hook atau lifecycle yang dieksekusi saat client melakukan hydratasi. Contohnya, panggil web_sys::console::log_1 dalam use_effect_with_deps hanya di cfg!(debug_assertions) untuk mencatat props awal yang diterima.
  • Inspeksi DOM: Bandingkan DOM server (hasil HTML) dengan DOM saat hydratasi selesai. Browser devtools memberi tahu node mana yang berubah. Fokus pada atribut seperti value, data-*, atau teks yang dibangun dari state.
  • Flag environment: Tambahkan flag runtime custom seperti HYDRATION_DEBUG untuk menyalakan log dan snapshot props sebelum hydratasi. Ini memudahkan kita melihat perbedaan antara state server dan client tanpa injeksi penuh ke produksi.

Dengan data ini, Anda bisa mengidentifikasi apakah mismatch berasal dari waktu lokal, data fetch yang belum tersedia, atau efek samping client-only.

Penyebab Umum dan Cara Menanganinya

1. State Dinamis Berbeda

State yang tergantung waktu lokal atau random harus dihindari pada render server. Misalnya, menampilkan "Waktu sekarang" di server akan berbeda setelah client menghidupkan halaman. Solusinya adalah:

  • Siapkan state default dari props server (misalnya, timestamp dari backend) lalu gunakan use_effect untuk memperbarui hanya setelah hydratasi selesai.
  • Gunakan pola placeholder yang statis di server dan update dengan efek client, tapi pastikan render awal sama. Jangan render nilai yang langsung berubah.

2. Data Fetch Terlambat

Jika komponen sudah dirender di server dengan data yang tersedia, pastikan client juga menerima data awal yang sama saat hydratasi. Teknik yang bisa digunakan:

  • Kirim data prefetch lewat props yang di-serialize ke HTML (misalnya window.__INITIAL_DATA__) lalu Baca di client.
  • Jika data fetch baru dilakukan client-side, gunakan loading state yang sama pada server dan client sehingga DOM tidak berubah saat data tiba.

3. Efek Samping yang Mengubah DOM

Efek seperti pengaturan fokus atau manipulasi DOM manual dapat menyebabkan perbedaan hydratasi. Pastikan:

  • Efek hanya dijalankan setelah hydratasi (gunakan flag hydrated atau use_effect_with_deps yang bergantung pada use_state yang menandakan hydratasi selesai).
  • Jangan mengubah atribut atau struktur HTML yang sudah dirender tanpa alasan. Bila perlu modifikasi, lakukan setelah client ready dan batasi dampaknya.

Strategi Mempertahankan Snapshot State

Supaya state tetap konsisten, gunakan pola "snapshot" bagi data penting:

  • Props snapshot: Taruh state awal di props komponen dan pastikan tidak diganti selama hydratasi. Contohnya, kirim string lokal tertentu dari server sebagai props.
  • State rekonsiliasi: Ketika client melakukan fetch ulang, cari cara untuk memeriksa apakah data baru sama dengan snapshot dan hanya update bila berbeda signifikan.
  • Disable hydratasi selektif: Di bagian UI tertentu yang sulit disinkronkan, pertimbangkan untuk me-render ulang di client sepenuhnya (misalnya menggunakan yew::platform::spawn_local untuk mount ulang) dan biarkan server hanya menyediakan skeleton.

Dengan pendekatan ini, Anda menghindari perubahan DOM tak terduga saat client mengambil alih.

Contoh Pola Kode: Props Konsisten

Berikut pola komponen sederhana yang menjaga atribut tetap sama dan menunda pembaruan waktu sampai hydratasi selesai:

#[derive(Properties, PartialEq)]
pub struct ClockProps {
    pub initial_iso: String,
}

#[function_component(Clock)]
pub fn clock(props: &ClockProps) -> Html {
    let time = use_state(|| props.initial_iso.clone());
    {
        let time = time.clone();
        use_effect_with_deps(
            move |_| {
                let interval = gloo_timers::callback::Interval::new(1000, move || {
                    let now = chrono::Utc::now().to_rfc3339();
                    time.set(now);
                });
                || drop(interval)
            },
            (),
        );
    }

    html! {
        
    }
}

Server mengisi initial_iso dengan snapshot waktu saat render. Client kemudian memperbarui hanya setelah hydratasi, menjamin render awal sama dan DOM tidak bergeser. Jika Anda ingin menunggu sampai hydratasi selesai sebelum memulai interval, tambahkan state hydrated yang di-set di use_effect pertama kali.

Tips Debug dan Kesalahan Umum

  • Jangan render waktu atau nilai random langsung: Selalu kirim snapshot dari server, atau gunakan placeholder statis lalu update dengan efek kosong.
  • Perhatikan atribut default: Misalnya checked atau value di form harus sama di server dan client karena Yew tidak menyinkronkan secara otomatis.
  • Gunakan debugging flag: Warna console log berbeda untuk server/client untuk menelusuri kapan nilai berubah.

Dengan pola diagnosa dan perlindungan state yang konsisten, Anda bisa menghindari render mismatch dan menjaga UI tetap stabil saat hydratasi di Rust Yew SSR.