React key, solusi rerender uncontrolled component
Key adalah koentji
Huwaaa lama gak nulis topik tentang kode gaes. Ini kebetulan ada sekelebatan trik usang, sayang kalau cuma dibiarin lewat. Siapa tahu masih ada yang belum ngeh alias gak sadar. Daripada X Kode mulu yang update, yaudin nulis ini aja. Lanjuuuuttt… 🙃
key
Di React, fungsi key
adalah untuk mengoptimalkan proses render pada daftar komponen. Misalnya ada komponen yang dirender dengan 100 iterasi. Maka setiap komponen dengan key
akan dirender dan tersimpan sebagai “cache”. Sehingga jika ada render ulang, komponen tidak akan dirender, melainkan diambilkan dari yang sudah dirender sebelumnya.
// users berjumlah 100 data
users.map((user) => {
return <User key={user.id} />;
});
Pada contoh di atas, komponen User hanya akan dirender sekali di setiap iterasi. Jika ada render ulang, komponen User tidak akan dirender lagi. Sehingga waktu yang dibutuhkan jauh lebih singkat dibanding saat pertama kali komponen User dirender, meskipun sama2 dalam 100 iterasi.
key
wajib menggunakan nilai yang dapat diprediksi untuk menghasilkan optimalitas yang diharapkan.
Uncontrolled component
Uncontrolled component adalah komponen yang perilakunya tidak dikontrol oleh props. Karena tidak ada props yang mempengaruhi, maka uncontrolled component memiliki sifat yang sama seperti saat daftar komponen diberi key
. Komponen tidak akan render ulang, meskipun induk komponen dirender ulang.
// uncontrolled component
<form>
<input name="fullname" />
</form>
// controlled component
function Form() {
const [fullname, setFullname] = useState('');
return <form>
<input
name="fullname"
value={fullname}
onChange={(e) => {
setFullname(e.target.value);
}}
/>
</form>;
}
Contoh di atas cukup jelas menggambarkan perbedaan uncontrolled component dan controlled component. Uncontrolled component tidak ada props sama sekali. Sedangkan controlled component value pada input fullname
dipengaruhi oleh state fullname
. Dimana state fullname
dikontrol oleh setFullname
.
// uncontrolled component
<form
onSubmit={(e) => {
// langsung kirim seluruh input, tanpa perlu state
submit(e.currentTarget);
}}
>
<input name="fullname" />
</form>
// controlled component?
Nah, dengan contoh kasus di atas, akhirnya kita tahu bahwa penggunaan controlled component menjadi berlebihan. Jika kita tidak membutuhkan komponen yang bisa dikontrol, maka sudah semestinya kita tidak menggunakan controlled component. Selain tidak berguna, render ulangnya turut membebani.
Uncontrolled custom component
Tapi bagaimana dengan custom component? Perilaku custom component kan tidak sama dengan component dasar? 🤔
Betul. Mari kita tengok komponen Select dari shadcn/ui (basisnya menggunakan Radix UI) di bawah ini.
<form>
<Select name="capres">
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="abdel">Abdel</SelectItem>
<SelectItem value="temon">Temon</SelectItem>
<SelectItem value="muklis">Muklis</SelectItem>
</SelectContent>
</Select>
<select name="capresBiasa">
<option value="abdelBiasa">Abdel Biasa</option>
<option value="temonBiasa">Temon Biasa</option>
<option value="muklisBiasa">Muklis Biasa</option>
</select>
<button type="reset">Reset</button>
</form>
Dalam skenario reset form, komponen Select di atas tidak akan ter-reset sebagaimana komponen select biasa. Saat sudah dipilih Abdel, di-reset pun tetap akan Abdel yang terpilih.
Rerender menggunakan key
Jika pada awal tulisan saya menekankan key
sebagai upaya agar proses render di daftar komponen menjadi optimal, dalam kasus reset uncontrolled custom component bisa kita manfaatkan juga gaes.
Pada kasus sebelumnya, form tidak digunakan untuk menyimpan data, tapi untuk menyaring data. Pada kasus form yang digunakan untuk menyimpan data, sederhananya kita bisa menggunakan controlled component agar custom component dapat di-reset. Tapi pada kasus menyaring data, kita akan cenderung mengirimkan data tersebut melalui query string. Maka untuk alasan efisiensi, yang kita manfaatkan adalah query string tersebut, tanpa perlu state tambahan.
<form
onReset={() => {
submit(initialSearch)
}}
>
<Select name="capres" defaultValue={search.get('capres')}>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="abdel">Abdel</SelectItem>
<SelectItem value="temon">Temon</SelectItem>
<SelectItem value="muklis">Muklis</SelectItem>
</SelectContent>
</Select>
<select name="capresBiasa" defaultValue={search.get('capresBiasa')}>
<option value="abdelBiasa">Abdel Biasa</option>
<option value="temonBiasa">Temon Biasa</option>
<option value="muklisBiasa">Muklis Biasa</option>
</select>
<button type="reset">Reset</button>
</form>
Pada kondisi di atas, reset yang dilakukan akan mengembalikan kondisi select biasa dalam kondisi semula, tapi tidak dengan Select. Di sini perlunya key
.
<Select
name="capres"
defaultValue={search.get('capres')}
key={'capresKey'+search.get('capres')} // sikat gaes
>
Sifat
key
kurang lebih sama seperti atributid
di element HTML. Sehingga pastikan unik, tidak boleh ada yang sama.
Dengan begitu, komponen Select tetap sebagai uncontrolled component, namun dengan kemampuan seperti controlled component dari sisi optimistik render-nya. Segala render ulang dari induk, juga akan render ulang komponen Select. Jika semua key
sudah pernah di-render, maka setelahnya komponen Select bersifat seperti daftar komponen yang saya maksud di atas, tidak ada render lagi. Sehingga tetap lebih optimal daripada controlled component.
Di bawah ini saya lampirkan contoh kasus di atas ya gaes. Cukup sekian, semangat mengkode ☕️