Hydration mismatch Livewire terjadi ketika DOM yang diharapkan oleh klien tidak cocok dengan HTML yang dihasilkan server. Untuk komponen stateful dengan data dinamis, solusinya adalah memastikan state dan markup konsisten setiap kali Livewire melakukan render. Artikel ini langsung menjelaskan mengapa mismatch muncul, bagaimana cara memeriksa state, serta tindakan konkret memperbaiki dan memverifikasinya.

Penyebab Utama Hydration Mismatch pada Komponen Stateful

Hydration mismatch sering muncul setelah perubahan route, fetch data asynchronous, atau ketika komponen kembali menggunakan state lama tanpa sinkronisasi dengan data baru. Kondisi paling umum adalah:

  • Markup berubah tanpa memperbarui state. Misalnya, menampilkan daftar dari API yang return order berbeda setiap render atau menambahkan elemen tergantung pada response yang tidak di-cache.
  • Komponen turunan dalam loop tidak memiliki wire:key. Livewire membandingkan DOM lama dengan baru menggunakan key; tanpa key unik, patching DOM dapat mencampuradukkan elemen.
  • Route/parameter berubah tapi state tidak di-reset. Ketika Anda menavigasi route berbeda yang menggunakan komponen sama (misalnya page filtering), komponen bisa mewarisi state lama sehingga HTML server berbeda dengan DOM klien yang sudah ada.
  • Properti yang seharusnya statis diubah. Properti seperti public $slug yang memengaruhi render harus di-protect agar tidak diubah secara diam-diam saat lifecycle berjalan.

Langkah Debug: Log State di mount dan Bandingkan HTML

Hydration mismatch memberi tahu Anda ada perbedaan, tapi tidak selalu menyebut apa. Proses debugging sederhana bisa dilakukan langkah demi langkah:

  1. Log state segera setelah mount(). Kalau state awal berbeda dengan harapan, mismatch akan muncul di render pertama. Tambahkan log seperti berikut:
use Illuminate\Support\Facades\Log;

public function mount(array $payload = [])
{
    $this->filters = $payload['filters'] ?? [];
    Log::debug('mount hydration', ['filters' => $this->filters]);
}

Ini membantu memastikan data yang diterima sesuai dengan payload server.

  1. Bandingkan HTML server dengan DOM klien. Pada DevTools, lihat sumber HTML respons Livewire pada tab Network. Cocokkan struktur dan nilai atribut dengan DOM yang sedang terpasang. Perbedaan kecil seperti atribut data-testid yang berubah bisa menjadi indikasi state tidak sinkron.
  2. Gunakan updated() untuk menangkap perubahan. Jika state berubah setelah interaksi, log properti yang memicu mismatch:
public function updatedSelectedSort($value)
{
    Log::debug('updated sort', ['value' => $value, 'renderedAt' => now()->toIsoString()]);
}

Catat waktu render untuk memastikan perubahan terjadi sebelum Livewire melakukan diff.

Solusi Sinkronisasi State Dinamis

Setelah menemukan properti atau loop yang menyebabkan mismatch, terapkan solusi berikut:

  • Gunakan wire:key untuk setiap item dinamis. Livewire memerlukan key yang konsisten agar DOM patching nondestructive. Contoh melalui loop:
<div wire:key="order-{{ $order->id }}">
    <span>{{ $order->status }}</span>
</div>

Pastikan nilai key tetap sama antar render agar Livewire tahu elemen mana yang diperbarui.

  • Manfaatkan lifecycle mount() dan updated(). Selalu set state awal di mount(), lalu gunakan updated() untuk memeriksa transisi properti. Jangan mengubah state penting secara langsung di render karena render dipanggil ulang akibat diff—pakai method terpisah.
  • Proteksi properti read-only. Jika ada properti seperti slug/ID yang tidak boleh berubah setelah render pertama, buat method setter khusus atau gunakaan protected $queryString dengan validasi, lalu hindari modifikasi tanpa kontrol.

Solusi minimal memperbaiki mismatch: tambahkan wire:key pada loop, pastikan mount() men-set state awal, dan jangan ubah properti itu bebal di render().

Verifikasi dengan Laravel Debugbar dan Dusk

Laravel Debugbar sangat membantu melihat query atau state yang terjadi selama render Livewire. Anda bisa menambahkan log atau timing pada komponen, lalu meninjau di Debugbar untuk memastikan:

  • Tidak ada query ganda yang mengubah state antara render server dan klien.
  • State yang dijadikan parameter terlihat sesuai di stack trace dan log.

Untuk memastikan tindakan pengguna menghasilkan state yang konsisten, jalankan Dusk untuk simulasi route change dan interaksi. Gunakan assertion seperti:

$browser->visit('/orders/create')
    ->assertSee('Create Order')
    ->press('@publish')
    ->pause(500)
    ->assertSee('Order published');

Jika Dusk menunjukkan UI yang benar namun Livewire log menunjukkan mismatch, Anda kemungkinan besar melihat state yang berubah di lifecycle sebelum DOM di-render ulang.

Checklist Sebelum Deploy

Sebelum meluncurkan perubahan, pastikan:

  • View dan session dicache ulang. Jalankan php artisan view:cache dan php artisan config:cache bila perlu, agar markup terbaru dipakai. Untuk session file, jalankan php artisan session:table jika membutuhkan migrasi state.
  • State direset bila komponen tetap hidup. Gunakan $this->reset() atau $this->resetValidation() setelah submit agar state lama tidak terbawa.
  • Periksa caching Livewire di server. Jika menggunakan SSR atau caching response, pastikan cache invalidation berjalan saat route berubah.

Dengan checklist ini, merilis komponen Livewire stateful menjadi lebih dapat diprediksi dan mengurangi risiko hydration mismatch di lingkungan produksi.