Skip to content

Badge [2] di Sidebar Bukan Item Kedua yang Terlihat

Adityo Guni Waluyo

Badge rujukan di daftar sumber dihitung lewat indexOf dari array asli, bukan posisi iterasi list hasil filter, supaya nomornya tetap nyambung.

Ringkasan

Nomor badge sumber di sidebar ternyata dihitung dari indeks array hasil filter, sehingga bergeser tiap ada knowledge entry yang disaring dan tidak cocok lagi dengan rujukan di jawaban chat. Fixnya satu baris: nomor dihitung langsung dari array asli pakai turn.sources.indexOf(src) + 1. Pelajarannya, posisi render itu milik tampilan, nomor rujukan itu milik data.

Pas review diff refactor list sumber di panel chat, mata saya berhenti di satu pola yang keliatan sepele: nomor badge dihitung dari posisi iterasi list. List-nya sendiri baru aja disaring, knowledge entry-nya keluar dari sidebar. Saya coba telusuri satu klik lewat kepala: rujukan [2] di jawaban bakal mendarat di kartu yang salah.

Dikit konteks soal posisi list itu. Panel tengah sekarang yang pegang list lengkap, termasuk state expand-nya (sejak commit 1d35066). Sidebar tinggal nampilin web sources, dengan knowledge entry disaring lewat visibleSources, hasil nyaring turn.sources pake isKnowledgeSlug. Knowledge entry emang nggak muncul di sana. Keliatan selesai.

Dugaan pertama saya pas ngebaca diff: ah, ini pasti masalah DOM id. Salah format, atau turn id-nya ketuker. Tapi id kartunya malah dirancang bener, dengan nomor dari array asli. Justru di situ saya berhenti mikir: kalo nomor badge dihitung dari list hasil filter, id-nya ikut salah semua, format sebagus apa pun nggak nolong. Yang bahaya bukan bentuk id-nya, tapi angka yang nempel di dalamnya.

Badge [2] di sidebar nunjuk kartu ke-2 versi layar. Rujukan [2] di jawaban maksudnya item ke-2 versi data.

Dua kalimat ini beda, dan di situlah jebakannya. Nomor badge dihitung dari map((src, i) => ...) di atas array hasil filter. Padahal filter nggak cuma nyembunyiin item; dia bikin array baru yang urutannya kompak dari nol lagi ([3]). Begitu satu knowledge entry kebuang, semua web source di belakangnya naik satu posisi. Web source pertama yang lolos filter dapat nomor 1, padahal [1] di jawaban menunjuk knowledge entry yang justru disaring. Kehilangan satu kartu, kabur semua nomor di belakangnya.

Posisi render bukan indeks data

react.dev nulisnya: keys bikin React tahu item array mana yang cocok sama komponen mana, karena posisi item dalam list itu identitasnya saat render ([1]). Kecocokan itu sah buat list yang dirender. Tapi nomor rujukan di jawaban chat lahir lebih awal, dari urutan turn.sources asli, jauh sebelum urusan tampilan. Dua dunia yang beda, dan saya nggak sengaja nyamain keduanya.

Satu baris yang jujur

Fixnya satu baris. Badge dan id kartu sekarang dihitung dari array asli:

const visibleSources = turn.sources.filter(
  (src) => !isKnowledgeSlug(src.slug),
)

// per src di visibleSources — nomor dari array ASLI:
const n = turn.sources.indexOf(src) + 1
// badge: {n}, id kartu: `ai-source-${turnId}-${n}`

indexOf balikin posisi pertama elemen di array ([2]), jadi nomor yang muncul di sidebar selalu sama dengan nomor yang dipakai jawaban. Filter boleh ngubah tampilan; dia nggak boleh ikut-ikutan menomori.

Jujur ada cara lain yang lebih bersih soal performa: bawa pasangan item dan indeks dari awal, map dulu ke bentuk { src, i } baru difilter. Nggak ada dobel lookup. Tapi turn.sources isinya beberapa item doang, dan slug per turn emang unik. Kalo suatu hari list bisa punya elemen dobel, indexOf bakal kasih indeks pertama ke semuanya, dan pas itu saya ganti ke pasangan eksplisit. Sekarang saya milih satu baris yang paling gampang kebaca.

Sisa refactor lain yang kebawa: karena expand-nya udah pindah ke panel tengah, kartu di sidebar dirender tanpa perilaku buka-tutup. Klik di kartu sidebar sekarang tugasnya satu, buka sumbernya langsung. Sidebar makin murni jadi daftar rujukan, panel tengah jadi tempat baca. Dua peran, dua tempat, nggak ada lagi yang dobel.

Aturan yang saya pegang setelah ini: kalo angka di UI dipakai sebagai rujukan, angka itu harus lahir dari data aslinya, bukan dari array yang udah disaring. Posisi render itu cuma urutan gambar di layar; dia boleh berubah tiap filter, dan justru makanya nggak boleh dipinjam jadi nomor rujukan. Posisi render itu milik tampilan. Nomor rujukan itu milik data.

Sekarang tiap ketemu map((src, i) di atas list hasil filter, saya berhenti sebentar dan nanya satu hal: i ini indeks apa, dan ada yang manggil dia "nomor tiga" nggak?

Sumber

Artikel terkait