Textarea React Diotomasi, Tapi Diam Saja
Teks terlihat di layar tapi React tidak tahu ada perubahan. Kenapa manual assignment gagal, dan cara bypass controlled component pakai native prototype setter.
Ringkasan
Kalau isi textarea lewat assignment biasa, React tidak sadar karena setter aslinya sudah diganti wrapper React yang menelan event buatan. Solusinya panggil setter asli dari HTMLTextAreaElement.prototype, lalu dispatch event input dengan bubbles: true supaya React menangkap perubahannya. Tombol kirim yang cuma ikon tanpa teks? Cukup tekan Enter lewat API keyboard Playwright.
Saya sedang ngirim prompt panjang ke chat DeepSeek lewat script Python. Teks masuk textarea , saya lihat di layar, huruf satu-satu terisi. Tapi tombol kirim tetap mati. Begitu saya paksa kirim pakai regex "Send", URL chat berubah jadi /c/new-chat kosong, tanpa isi sama sekali.
Dugaan awal saya: referensi elemen di Camofox sudah stale: DOM React yang sering re-render, DOM node lama sudah lepas dari React tree. Saya verifikasi ulang: ref-nya valid, elemen masih ada. Maka dugaan kedua, bug Camofox, juga ternyata salah. Teks memang sudah masuk ke DOM, tapi React sendiri tidak tahu bahwa ada perubahan.React Menimpa Setter, DOM Tetap Siluman
Ini yang banyak orang tidak sadari: React tidak pakai DOM sebagai sumber kebenaran untuk controlled component. value prop adalah state; DOM hanya cerminan visual. Begitu React memasang textarea terkontrol, framework ini mengganti setter value di elemen itu : bukan di prototipe, tapi di instance. Kalau saya paksa assignment biasa, browser memang menampilkan teks di layar. Tapi React tidak menerima perubahan itu sebagai input yang valid, karena setter yang dijalankan bukan setter browser asli, melainkan wrapper React yang memutuskan apakah perubahan ini "sah" , dan karena saya bukan user yang mengetik, perubahan itu dianggap tidak ada [1].
Solusinya: Lewati React, Langsung ke Prototipe Browser
Karena React mengganti setter di instance elemen, satu-satunya cara menembus lapisan itu adalah mengambil setter asli dari prototipe HTML browser. Caranya:
// setter asli dari prototipe textarea browser, BUKAN dari instance React
const nativeSetter = Object.getOwnPropertyDescriptor(
HTMLTextAreaElement.prototype, 'value'
).set;
// paksa browser memperbarui nilai DOM langsung, React tidak campur tangan
nativeSetter.call(textarea, promptText);
// beri tahu React ada perubahan baru
textarea.dispatchEvent(new Event('input', { bubbles: true }));
Pertama kali saya baca ini, saya kira itu trik kuno untuk React lama. Ternyata jawaban yang sama dikonfirmasi di SO [2] dengan voting cukup tinggi, dan kredit solusi ini sebenarnya ditujukan ke tim Cypress, yang menghadapi masalah serupa saat test automation [2b]. Dalam pengujian end-to-end saya: teks masuk, tombol kirim aktif, URL tidak lagi /c/new-chat palsu. Satu komponen sihir: bubbles: true di event input; tanpa itu, event tidak merambat ke parent yang React gunakan untuk menangkap perubahan.
Setelah nilai berhasil masuk, ada masalah kedua: di DeepSeek, tombol kirim cuma ikon tanpa label teks. Regex pencarian teks "Send" tidak mungkin menemukannya, tidak ada text node sama sekali. Playwright sendiri merekomendasikan API keyboard untuk kasus seperti ini [3]. Perintah press memancarkan urutan keyboard event lengkap dari keydown sampai keyup. Ini dikenali universal oleh hampir semua chat UI sebagai sinyal submit. Setelah saya ganti dari cari tombol kirim ke menekan Enter, tidak ada lagi gagal kirim.
Kombinasi native setter + press Enter ini menjadi rutin di script riset saya. Framework target tidak penting, yang penting cara kerja controlled component di baliknya.Satu catatan sebelum ditiru. Native setter plus dispatchEvent adalah workaround terhadap implementasi React, bukan API resmi. Ini artinya tidak ada jaminan kompatibilitas lintas versi React, meski untuk React 15.6 sampai 19, cara ini konsisten ditemukan bekerja. Satu caveat lain: event buatan punya isTrusted = false di JavaScript, berbeda dari event asli user. Untuk automation yang hanya butuh memicu perubahan state (seperti mengisi chat prompt), ini tidak jadi masalah. Tapi kalau ada library yang memeriksa isTrusted secara eksplisit, event sintetik ini bisa ditolak. Saya belum temukan kasus itu di DeepSeek, tapi untuk aplikasi lain ada baiknya diuji dulu.
Sumber
- React issue #10135 — "dispatchEvent on input/textarea is ignored" (2017)
- SO 23892547 — "What is the best way to trigger change or input event in React" (Cypress issue #647, solusi native setter)
- Playwright docs: Actions — Keyboard input
- React docs: textarea (controlled component, value + onChange)