Hydration mismatch pada aplikasi Server-Side Rendering (SSR) seperti Next.js terjadi ketika pohon DOM hasil render server berbeda dengan representasi virtual DOM pada client. Dalam domain aplikasi perbankan atau akuntansi, inkonsistensi ini memicu layout shift, rusaknya struktur tabel, hingga ancaman fatal salah baca nominal uang.
Sebagaimana diulas pada catatan sejarah ekonomi di The Daily Economy, ledger kuno bangsa Sumeria diukir pada lempengan tanah liat sebagai representasi transaksi abadi yang immutable. Data finansial historis modern memegang prinsip serupa: angka, debit, kredit, dan waktu tidak boleh berubah begitu tercatat. Masalah timbul ketika representasi data abadi tersebut harus diproyeksikan melalui runtime yang berbeda antara Node.js di server dan browser pengguna.
Akar Masalah Hydration Error pada Data Finansial
Penyebab utama inkonsistensi rendering pada tabel ledger terbagi dalam tiga faktor teknis:
- Perbedaan Locale Intl.NumberFormat: Server container kerap berjalan dengan environment default
en-USatauPOSIX, sementara browser client menggunakan locale pengguna (misalid-ID). Angka1000000dirender sebagai$1,000,000.00di server danRp 1.000.000,00di browser. - Timezone Offset Historis: Server berjalan pada UTC (
+00:00), sementara client berjalan pada timezone lokal (misal WIB+07:00). Timestamp ISO2024-03-31T22:00:00Zdirender server pada tanggal 31 Maret, namun browser menampilkan tanggal 1 April. - Mutasi State Lokal Saat Mount: Pembacaan properti client-side seperti
localStorageatau konversi dinamis langsung di dalam body komponen sebelum mount selesai.
Strategi Mengatasi Hydration Mismatch
1. Two-Pass Rendering dengan Custom Hook
Pendekatan ini menunda rendering elemen dinamis berbasis locale browser hingga proses hidrasi awal selesai secara deterministik.
import { useState, useEffect } from 'react';
export function useHydrated() {
const [hydrated, setHydrated] = useState(false);
useEffect(() => {
setHydrated(true);
}, []);
return hydrated;
}2. Format Deterministik dan Atribut suppressHydrationWarning
Untuk teks nominal sederhana, passing locale eksplisit dari server adalah pilihan teringan. Jika string teks leaf node tetap berisiko mismatch akibat perbedaan engine browser, gunakan suppressHydrationWarning secara terbatas pada tag target.
// Komponen LedgerAmount
interface Props {
amount: number;
currency: string;
locale?: string;
}
export function LedgerAmount({ amount, currency, locale = 'en-US' }: Props) {
const formatted = new Intl.NumberFormat(locale, {
style: 'currency',
currency,
}).format(amount);
return <span suppressHydrationWarning>{formatted}</span>;
}suppressHydrationWarning hanya bekerja satu level ke bawah pada elemen teks native. Atribut ini tidak menyembunyikan error jika struktur tag anak berbeda.3. Arsitektur Data Berbasis UTC Konsisten
Struktur data ledger yang dikirim dari server harus membawa string waktu deterministik. Format tampilan di-render sebagai UTC murni pada pass pertama, atau gunakan format ISO standar sebelum dikonversi di client.
interface LedgerEntry {
id: string;
timestamp: string; // ISO 8601 UTC
amount: number;
}
export function LedgerRow({ entry }: { entry: LedgerEntry }) {
const isHydrated = useHydrated();
// Server & First-pass Client: Pakai representasi UTC deterministik
// Client Hydrated: Pakai locale lokal pengguna
const displayDate = isHydrated
? new Date(entry.timestamp).toLocaleString()
: new Date(entry.timestamp).toISOString().replace('T', ' ').slice(0, 19) + ' UTC';
return (
<tr>
<td>{displayDate}</td>
<td>{entry.id}</td>
<td><LedgerAmount amount={entry.amount} currency="USD" /></td>
</tr>
);
}Trade-offs dan Kesimpulan
Pola Two-Pass Rendering menambah satu siklus re-render di browser setelah mount, yang menimbulkan sedikit visual swap. Alternatif yang lebih optimal tanpa re-render adalah memaksakan locale deterministik (seperti format akuntansi baku ISO) langsung dari backend API, atau mendeteksi preferensi locale pengguna via HTTP Accept-Language headers / cookies pada level server middleware.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!