Ketika React SSR disajikan melalui Express dan klien melakukan hydration, mismatch antara markup server dan state klien menyebabkan WAJIB dievaluasi sejak awal. Artikel ini membahas langsung cara mendiagnosis mismatch, menjaga state tetap konsisten, dan menyiapkan fallback yang minim berkedip ketika hydration gagal.

1. Mendiagnosis Hydration Mismatch

Hydration mismatch terjadi karena state atau props yang digunakan saat render server berbeda dengan yang diteruskan ke klien. Langkah pertama adalah membandingkan payload state server dan state yang diterima klien.

  • Snapshot state sebelum render: Saat Anda mempersiapkan HTML di server, serialisasikan state akhir (misalnya store.getState() pada Redux atau data query terkini) dan kirim melalui window global. Catat versi data tersebut.
  • Bandingkan di klien: Saat klien melakukan hydration, log state awal sebelum React membuat render. Periksa apakah ada perbedaan kunci, struktur, atau nilai, terutama data yang di-fetch saat build request.
  • Telusuri data fetching SSR: Gunakan middleware yang mencatat query atau API call yang terjadi selama render. Jangan hanya melihat data final — perhatikan apakah permintaan async dijalankan ulang di klien dengan parameter berbeda.

Debug tip: tambahkan header respons X-SSR-State yang berisi hash state. Saat mismatch terjadi, bandingkan hash tersebut dengan yang dihasilkan klien untuk mengetahui bagian mana yang berbeda.

2. Menjaga Sinkronisasi State Server dan Klien

Serialisasi dan inject data eksplisit

Pastikan server mengirimkan state lengkap yang digunakan pada render: jangan hanya mengandalkan fetching di browser.

app.get('/home', async (req, res) => {
  const preloadedData = await fetchHomePageData(req.user);
  const appMarkup = renderToString();
  const serializedState = JSON.stringify(preloadedData).replace(/

Rehidrasi klien harus membaca window.__INITIAL_DATA__ dan menggunakannya sebagai state awal untuk menghindari fetching ulang yang mengubah data. Ini juga mencegah permintaan ganda yang bisa menimbulkan mismatch.

Memastikan deterministic data fetching

Gunakan helper atau hook untuk menandai fetch yang sudah dilakukan server. Contoh sederhana: middleware Express menyimpan metadata request dan menyelipkannya ke response sehingga klien tahu fetch mana yang perlu dilewatkan.

3. Pola Middleware dan Route Handler untuk State Konsisten

Gunakan middleware Express untuk mengumpulkan data dependensi sebelum render, agar semua route handler yang memanggil render memiliki akses terhadap data yang sama.

async function withPageData(handler) {
  return async (req, res, next) => {
    try {
      const pageData = await gatherDependencies(req);
      res.locals.pageData = pageData;
      return handler(req, res);
    } catch (err) {
      next(err);
    }
  };
}

app.get('/dashboard', withPageData((req, res) => {
  const appMarkup = renderToString();
  res.send(renderFullPage(appMarkup, res.locals.pageData));
}));

Dengan pendekatan ini, semua route SSR membaca data dari res.locals, sehingga data dependensi konsisten dan tidak bergantung pada logika render in-line.

4. Teknik Fallback untuk Mengurangi ‘Kedip’ UI

Hydration yang gagal menyebabkan React menolak DOM server dan merender ulang, memicu “kedipan” UI. Untuk meminimalisirnya:

  • Rancang fallback UI minimal: tampilkan skeleton statis untuk komponen yang bergantung pada data dinamis. Skeleton itu tetap ada saat hydration gagal, sehingga tidak ada lompatan layout drastis.
  • Gunakan rehydration-safe render: saat data belum ready, biarkan komponen menunggu fetch di klien dengan placeholder yang identik dengan markup server.
  • Deteksi mismatch via API: ketika server mendeteksi bahwa state klien berbeda (misalnya hash), kirimkan flag pada response header X-Hydration-Status. Klien bisa memutuskan apakah memicu re-fetch parsial atau sekadar log peringatan.

Fallback juga memungkinkan Anda menyimpan fragment SSR tanpa memaksa rerender penuh, cukup update bagian yang bermasalah setelah data baru tersedia.

5. Observabilitas dan Logging untuk Deteksi Dini

Observabilitas membantu mengidentifikasi mismatch sebelum pengguna melaporkannya.

  • Log state hash di server dan klien: Buat middleware Express yang mencatat hash dari data final. Di klien, tambahkan middleware konsol untuk mengirim log ke server ketika mismatch terdeteksi.
  • Tambahkan header debugging: Kirim header seperti X-SSR-Data-Version atau X-Fetched-At. Middleware klien bisa memeriksa header ini untuk memastikan data yang sama dipakai pada hydration.
  • Gunakan monitoring error: Tangkap warning React hydration (misalnya dari console.error) dan kirimkan ke sistem observability Anda. Pengelompokan log ini membantu melihat pola mismatch di berbagai route.

Dengan observabilitas, Anda bisa melihat apakah mismatch hanya terjadi di data tertentu, di kondisi login khusus, atau hanya saat cache tidak sinkron.

Kesimpulan

Hydration mismatch di React SSR dengan Express bukan masalah yang tidak bisa dipecahkan: dengan diagnosis yang sistematis, sinkronisasi state eksplisit, struktur middleware yang andal, strategi fallback UI tanpa kedipan berlebihan, serta observabilitas yang lengkap, Anda dapat memastikan pengalaman pengguna tetap konsisten. Terapkan satu pendekatan pada satu waktu, dan pastikan setiap perubahan data dicek ulang di server-klien agar mismatch terseleksi lebih awal.