Selama lebih dari empat hari, fitur search overlay di sebuah situs editorial yang saya bangun mengembalikan nol hasil di produksi. Bukan error, bukan layar putih — cuma "tidak ada yang cocok" untuk setiap query, termasuk kata yang jelas-jelas ada di artikel. Di lokal? Sempurna. Itu kombinasi terburuk: bug yang hanya ada di tempat yang tidak bisa kamu inspeksi dengan nyaman.
Petunjuk pertama: satu key hilang
Saya buka console di produksi dan periksa objek global yang dipakai search:
Object.keys( window.X );
// → ['ajaxUrl', 'nonce', 'restBase', 'locale'] (4 key)Empat key. Mestinya lima. searchIndex — array yang berisi seluruh data pencarian — hilang. Tanpa index itu, setiap pencarian otomatis nol hasil. Pertanyaannya: kenapa index-nya ada di lokal tapi lenyap di produksi?
Penyebabnya: wp_localize_script me-reassign, tidak menggabung
Begini cara saya menyuntik data search. Di header, sebuah inline <script> mengisi index:
<!-- di header -->
<script>
window.X = window.X || {};
window.X.searchIndex = [ /* ...data pencarian yang besar... */ ];
</script>Lalu, di footer, saya melokalkan beberapa data konfigurasi ke objek global yang sama lewat wp_localize_script:
wp_localize_script( 'my-search', 'X', [
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'my_search' ),
'restBase' => esc_url_raw( rest_url() ),
'locale' => get_locale(),
] );Inilah jebakannya: wp_localize_script tidak menggabung ke objek yang sudah ada. Ia memunculkan deklarasi penuh yang me-reassign seluruh variabel:
// HTML yang dikeluarkan wp_localize_script, di footer:
var X = { "ajaxUrl": "...", "nonce": "...", "restBase": "...", "locale": "..." };var X = {…} itu menimpa total. Karena footer selalu dieksekusi setelah header, deklarasi ini menggusur objek window.X saya yang berisi searchIndex dan menggantinya dengan objek baru yang hanya punya empat key. Footer selalu menang. Index-nya tertimbun secara diam-diam.
Kenapa lokal aman? Urutan/timing enqueue dan caching bisa berbeda antar-lingkungan, tapi inti masalahnya struktural: begitu kedua snippet itu jalan dalam urutan header-lalu-footer, reassignment-nya pasti menghapus index. Saya cuma "beruntung" di lokal.
Perbaikannya: gabung dengan Object.assign, bukan reassign
Solusinya adalah berhenti membiarkan WordPress memunculkan var X = {…} yang me-reassign, dan menggantinya dengan merge yang tidak merusak. Saya tukar wp_localize_script dengan wp_add_inline_script yang menggunakan Object.assign:
$config = [
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'my_search' ),
'restBase' => esc_url_raw( rest_url() ),
'locale' => get_locale(),
];
wp_add_inline_script(
'my-search',
'window.X = Object.assign( window.X || {}, ' . wp_json_encode( $config ) . ' );',
'before'
);Dua detail krusial:
Object.assign( window.X || {}, … )menggabung key konfigurasi ke objek yang sudah ada, mempertahankansearchIndexyang sudah di-set di header.- Posisi
'before'memastikan inline script ini berjalan sebelum kode search yang membutuhkannya.
Setelah itu, Object.keys(window.X) kembali memunculkan kelima key, dan search overlay langsung berfungsi di produksi.
Aturan umum yang perlu diingat
wp_localize_script dirancang untuk mendeklarasikan sebuah objek data, bukan menambahnya. Setiap kali dua sumber menulis ke nama global yang sama, ia akan saling menimpa:
- Jangan pakai
wp_localize_scriptpada nama global yang sudah pernah diisi di tempat lain. Ia akan me-reassign, bukan menggabung. - Kalau perlu menyatukan data dari beberapa sumber ke satu global, pakai
wp_add_inline_script+Object.assign( window.X || {}, … ). - Perhatikan urutan eksekusi: footer berjalan setelah header. Apa pun yang dideklarasikan belakangan menang.
- Untuk mendiagnosis, bandingkan
Object.keys(window.X)di produksi vs lokal. Key yang hilang menunjuk langsung ke penimpaan, bukan ke data yang gagal di-load.
Pelajaran
Empat hari nol hasil, dan penyebabnya bukan logika search yang salah — melainkan satu pemanggilan WordPress yang me-reassign sebuah variabel global alih-alih menggabungnya. wp_localize_script memunculkan var X = {…} secara penuh, dan karena footer selalu menang atas header, ia menghapus index yang saya susun. Saat sebuah global tampak "kehilangan" data antar bagian halaman, jangan curigai data-nya dulu — curigai siapa yang menulis terakhir ke nama itu. Untuk menggabung, bukan menimpa: Object.assign.
