Kartu Sumber Pindah ke Bawah Jawaban Chat
Kartu sumber kini dirender inline di bawah jawaban chat; komponen dipakai ulang lewat named export dengan id unik per panel dan state expand terpisah.
Ringkasan
Commit 1d35066 merender kartu sumber secara inline di bawah jawaban panel tengah, kelihatannya cuma render ulang. Ternyata dua kontrak lama ikut kebawa: id harus unik di dokumen, makanya kartu tengah pakai sufiks, dan expanded state butuh pemilik jelas, jadi tiap panel punya state sendiri biar nggak sinkron. Pelajarannya, cek kontrak lama dulu sebelum bilang tinggal render ulang.
Jawaban chat di panel tengah udah muncul rapi, lengkap sama penjelasannya. Tapi pas saya perhatiin, kartu sumber yang jadi rujukan jawaban itu cuma nongol di sidebar. Pembaca yang scroll di panel tengah, tempat jawabannya berada, nggak akan pernah lihat kartu-kartu itu.
Dari situ ada commit kecil, 1d35066: kartu knowledge sekarang dirender inline di bawah jawaban, di panel yang sama dengan tempat orang baca. Sidebar tetep ada, cuma tugasnya fokus ke artikel dan halaman situs. Rencananya kedengeran sepele, dan pas mulai saya beneran mikirnya gitu. Yang belum saya sadari: mindahin render itu sekalian nyeret dua kontrak lama ke tempat baru.
Kayaknya cuma render ulang
Komponennya kan udah ada. SourceCard udah dirender ChatContextPanel buat sidebar, tinggal dipanggil lagi dari ChatOutputPanel. Selesai dalam semalam, begitu dugaan saya.
Tiga baris pertama emang segitu. Sisanya nggak.
Hal pertama yang keburu: ekspor. Konvensi React bilang file yang ngekspor satu komponen enak pakai default export, tapi begitu file itu ngekspor beberapa komponen sekaligus, pakai named export [2]. Begitu panel tengah ikut pakai SourceCard, file ChatContextPanel resmi punya dua komponen, jadi SourceCard ganti jadi named export. Perubahannya satu kata di depan, tapi artinya komponen itu sekarang bagian publik dari file, bukan detail internal.
Hal kedua: id. Dua panel sekarang merender kartu untuk sources yang sama. Kalo atribut id-nya disalin mentah, satu dokumen punya id ganda, dan MDN nyatakan id harus unik di seluruh dokumen [3]. Bug jenis ini nggak meledak di muka. Dia nyimpen diri buat bikin hal aneh belakangan, kayak anchor yang lompat ke tempat salah.
Solusinya sufiks. Kartu di panel tengah dapat id berupa prefiks ai-source-center diikuti turnId dan urutan kartu:
id={`ai-source-center-${turn.id}-${n}`}Sidebar punya ruang nama id-nya sendiri. Dua panel, dua set id, nggak tabrakan.
Expanded state punya rumah sendiri
Kartunya expandable. Klik, kontennya kebuka. Yang nggak saya pikirin pas mulai: expand-nya punya siapa.
Kalo state-nya dipinjem bareng dua panel, buka kartu di sidebar bakal bikin kartu yang sama kebuka di tengah. Bisa aja dijual sebagai fitur, tapi saya nggak mau. Dua panel itu dua konteks baca yang beda, dan expand di satu tempat nggak harus nular ke tempat lain.
Jadi expanded state sekarang milik panel tengah sendiri, isinya {turnId, slug}. Dua info dalam satu state: kartu mana yang kebuka, di turn mana. Dan karena variabel lokal biasa nggak persist antar render [1], ini wajib Hook state, titik.
Resetnya nggak pake useEffect tambahan. Pas render, panel tinggal ngecek apakah expanded.turnId masih nyambung ke turn.id yang lagi aktif. Beda turn, ya anggep aja nggak ada kartu yang kebuka. Pindah turn, state lupa sendiri.
Saya sempat mikir buat angkat state ini ke parent, sekalian dibagi rapi buat dua panel. Batal. Duplikasi state per panel itu harga murah buat perilaku yang jelas: panel A buka, panel B nggak ikut-ikutan. Sharing state worth it kalo dua panel emang dimaksudin sinkron, dan di sini nggak.
Satu filter kecil nutup semuanya: yang dirender inline cuma knowledgeSources, alias sources yang punya content. Nomor badge-nya tetap dihitung dari indeks asli di turn.sources, bukan urutan baru hasil filter, supaya rujukan [n] di jawaban masih nyambung ke kartu yang bener. Kartu kosong nggak layak nimpa ruang di bawah jawaban.
Yang kebawa dari commit ini sebenernya bukan caranya render kartu. Ekspor ulang, impor, render, itu bagian lima menit. Yang bikin mikir justru kontrak yang kebawa tanpa keliatan: id harus tetap unik di satu dokumen, state harus tetap punya pemilik yang jelas. Dua hal itulah yang gampang kelewat, dan mulai sekarang saya ngeceknya dulu sebelum ngomong "tinggal render ulang".
Sumber
1. [State: A Component's Memory, react.dev](https://react.dev/learn/state-a-components-memory) 2. [Importing and Exporting Components, react.dev](https://react.dev/learn/importing-and-exporting-components) 3. [HTML global attribute: id, MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/id)