Skip to content

Klik kartu React, peta ikut fokus: tiga jembatan ke MapLibre

Adityo Guni Waluyo

Event klik kartu React belum cukup untuk menggerakkan peta imperatif. Tiga jembatan kecil: registry ref, klik sintetis untuk popup, lalu flyTo.

Ringkasan

Marker di maplibre nggak punya API buka popup, jadi triknya dispatch klik sintetis yang nge-bubble ke peta biar popup muncul sendiri, plus flyTo buat zoom ke lokasinya. Pakai useRef buat nyimpen marker dan nahan request fokus sebelum peta selesai load. Tambah counter di dependency biar klik kartu yang sama tetap ke-trigger.

Saya klik kartu kantor di halaman depan, mengharapkan peta di sebelahnya zoom ke lokasi tersebut. Ternyata dia cuma diam. Nggak ada animasi, nggak ada popup. Cuma keheningan.

Reaksi pertama saya: paksa popup-nya muncul. Saya panggil popup.addTo(map) langsung, atau iseng ngatur style marker lewat DOM. Hasilnya nggak konsisten. Kadang muncul, kadang error, kadang posisinya nggak nyambung sama marker yang dimaksud. Saya sempat mengira ini bug di versi yang saya pakai.

Setelah ngecek source code maplibre-gl 4.7.1, ini bukan bug, tapi memang desainnya [1]. Marker nggak mendengarkan event klik pada elemen DOM-nya sendiri. Dia mendengarkan map.on('click') di level peta, lalu ngecek apakah originalEvent.target adalah elemen marker itu atau ada di dalamnya [1]. Artinya, nggak ada API resmi kayak marker.openPopup() di versi ini. Satu-satunya jalan untuk memicu mekanisme toggle popup bawaan library adalah klik yang bubble dari elemen marker ke peta [3].

Jembatan pertama: registry ref untuk race condition

Masalah pertama: race condition. Klik dari kartu React bisa datang sebelum peta selesai load. Event load peta sendiri baru fired begitu status internalnya true di dalam render loop [2]. Kalau flyTo dieksekusi sebelum itu, dia bakal menghilang tanpa jejak.

Solusinya dua useRef. Yang pertama jadi registry instance marker, yang kedua menampung permintaan fokus yang datang sebelum peta siap. Begitu event load peta fired, saya cek isi penampung itu; kalau ada, fungsi applyFocus dieksekusi. React sendiri mendokumentasikan ref sebagai tempat untuk nilai yang nggak berpengaruh ke output visual [4]. Pakai state biasa di sini malah memicu re-render saat peta lagi sibuk inisialisasi.

Jembatan kedua: klik sintetis plus flyTo

Di dalam applyFocus, urutannya begini. Pertama, dispatch klik sintetis ke elemen marker.

marker.getElement().dispatchEvent(new MouseEvent('click', { bubbles: true }));

Karena event ini dibuat dengan bubbles: true, dia merambat naik sampai container peta. Peta mengenali elemen marker sebagai target, dan logika togglePopup internalnya jalan sendiri tanpa saya menulis ulang positioning popup.

Kedua, gerakkan kameranya.

mapRef.current?.flyTo({ center: marker.getLngLat(), zoom: 13, essential: true });

Flag essential: true bukan gaya-gayaan. Kalau user mengaktifkan reduced motion di pengaturan aksesibilitas OS, transisi peta jadi instan kecuali opsi ini disertakan [2]. Terakhir, scrollIntoView di container peta, supaya di layar sempit popup nggak muncul di luar area pandang.

Jembatan ketiga: seq untuk klik berulang

Ada jebakan React klasik. User klik kartu A, peta fokus ke A. Dia klik kartu A lagi, dan efek nggak jalan, karena dependency useEffect dianggap nggak berubah.

Solusinya state penghitung seq. Setiap klik kartu menaikkan nilai seq satu, lalu itu masuk dependency array. Klik berulang pada kartu yang sama tetap dianggap perubahan yang valid, dan applyFocus jalan lagi. Pola counter sederhana ini yang menyelamatkan interaksi "bandingkan dua kantor bergantian".

Sentuhan akhir di popup

Dua detail kecil menutup cerita ini. Anchor popup dikunci ke anchor: 'bottom'. Default-nya auto, yang suka membalik posisi popup saat mentok tepi layar. Untuk popup kantor, saya mau dia selalu di atas titik marker, konsisten, nggak melompat.

Kedua, isi popup-nya dirombak: icon badge, label tipe kantor, blok alamat, lalu baris kontak dengan ikon. Popup jadi bukan cuma berfungsi, tapi enak dibaca pas muncul setelah animasi selesai.

Pelajaran terbesar dari komponen office-map.tsx ini: nggak semua hal bisa lewat state React. Kadang library imperatif butuh jembatan kecil yang menghormati cara dia bekerja, bukan cara kita ingin dia bekerja. Three bridges, dan klik kartu akhirnya menggerakkan peta.

Sources: [1] https://github.com/maplibre/maplibre-gl-js/blob/v4.7.1/src/ui/marker.ts [2] https://github.com/maplibre/maplibre-gl-js/blob/main/src/ui/map.ts [3] https://github.com/maplibre/maplibre-gl-js/blob/main/src/ui/marker.ts [4] https://github.com/reactjs/react.dev/blob/main/src/content/reference/react/useRef.md

Artikel terkait