Hydration mismatch terjadi ketika representasi virtual DOM hasil render server (SSR) tidak identik dengan virtual DOM awal yang dibangun oleh browser pada fase hydration. Dalam konteks aplikasi Web3 atau terdesentralisasi yang mengimplementasikan hashing konten per bab (Bab/Chunk ID) untuk identifikasi node P2P, galat ini sering muncul saat proses kalkulasi hash menghasilkan output berbeda di server dan client.

Akar Masalah: Mengapa P2P Hash Berbeda Antar Runtime

Pustaka hashing terdesentralisasi umumnya bergantung pada primitive biner yang sensitif terhadap platform eksekusi. Ada tiga penyebab utama mismatch pada skenario ini:

  • Perbedaan Dependensi Binary dan WebAssembly (Wasm): Engine SSR (Node.js/Bun) menggunakan modul native C++ atau bindings crypto sistem operasi, sedangkan browser memuat binary WebAssembly terpisah. Perbedaan inisialisasi modul asinkron ini menyebabkan hash di server dieksekusi secara instan, sedangkan di client nilai awal bernilai null atau menggunakan fallback sebelum binary selesai dimuat.
  • Inkonsistensi TypedArray dan Endianness: Struktur data biner seperti Uint8Array atau buffer parser dapat menghasilkan interpretasi byte yang berbeda jika serialisasi teks ke byte menggunakan implementasi non-standar antar lingkungan.
  • Non-deterministic Salt / Entropi Runtime: Penggunaan fungsi random internal untuk penentuan salt atau timestamp saat hashing membuat nilai hash berubah di setiap komputasi baru.

Strategi Solusi: Isolasi dan Payload Transfer

Untuk meniadakan error hydration tanpa menonaktifkan SSR, arsitektur sinkronisasi state harus mengadopsi salah satu dari dua pendekatan:

  1. Serialisasi State SSR: Hash dihitung sekali di server, lalu disuntikkan ke dalam HTML via payload script tag deterministik (misalnya tag <script id="__STATE__">) agar client langsung mengonsumsi nilai yang sama tanpa komputasi ulang saat hydration.
  2. Client-Only Execution: Isolasi elemen yang bergantung pada P2P hash agar hanya dirender setelah client selesai melakukan hydration (fase mounted), dengan menyajikan placeholder skeleton deterministik selama SSR.

Implementasi Kode: Penanganan P2P Hash Deterministik

Berikut adalah implementasi komponen React/Next.js minimal yang mengisolasi hashing modul browser, mencegah mismatch menggunakan flag deterministik, dan menyediakan fallback struktural.

import React, { useState, useEffect } from 'react';

// Simulasi hash loader P2P (misal: IPFS CID atau multihash Bab)
async function computeBabHash(content) {
  const encoder = new TextEncoder();
  const data = encoder.encode(content);
  const hashBuffer = await window.crypto.subtle.digest('SHA-256', data);
  const hashArray = Array.from(new Uint8Array(hashBuffer));
  return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
}

export function BabP2PNode({ babId, content, serverProvidedHash = null }) {
  const [hash, setHash] = useState(serverProvidedHash);
  const [isHydrated, setIsHydrated] = useState(false);

  useEffect(() => {
    setIsHydrated(true);

    // ponytail: hitung ulang di client hanya jika server tidak menyertakan hash
    if (!serverProvidedHash && typeof window !== 'undefined') {
      computeBabHash(content).then((calculated) => {
        setHash(calculated);
      });
    }
  }, [content, serverProvidedHash]);

  // SSR dan hydration render awal wajib identik
  if (!isHydrated && !serverProvidedHash) {
    return (
      <div className="bab-node placeholder">
        <span className="title">Bab {babId}</span>
        <span className="hash-label">Kalkulasi node hash...</span>
      </div>
    );
  }

  return (
    <div className="bab-node" data-node-id={hash}>
      <span className="title">Bab {babId}</span>
      <code className="hash-value">{hash}</code>
    </div>
  );
}

Skipped: Web Worker background hashing, add when ukuran content bab > 2MB.

Metode Alternatif: Serialisasi State via Server Payload

Jika hash node P2P wajib tersedia secara instan untuk kebutuhan web crawler tanpa fase transisi loading, lakukan komputasi hash murni pada layer backend (Node.js) menggunakan library native seperti node:crypto. Kemudian, lewatkan hasilnya sebagai server-side props.

Peringatan Keamanan: Jangan gunakan library WebCrypto polyfill yang tidak diverifikasi pada server runtime. Pastikan algoritma hashing (misalnya SHA-256 atau Blake3) menghasilkan representasi hex string yang sama persis antara Buffer.from() di Node.js dan SubtleCrypto di browser.

Langkah validasi integrasi:

  1. Jalankan build produksi SSR lokal.
  2. Matikan JavaScript di browser; pastikan markup placeholder atau hash server ter-render dengan benar.
  3. Nyalakan kembali JavaScript dan periksa Developer Tools console; pastikan tidak ada pesan Warning: Text content did not match.