ReactJS #9: useState Lebih Dalam – Counter, Input, Tampilan Dinamis


ReactJS #9: useState Lebih Dalam

Bermain dengan Counter, Input, dan Tampilan Dinamis

Halo, penyihir state! 🧙

Di artikel #7 kita sudah berkenalan dengan useState, si pengelola data yang bisa berubah. Sekarang kita akan menggali lebih dalam. Kita akan belajar cara mengupdate state dengan aman, menyimpan data yang lebih kompleks (seperti objek dan array), serta membuat tampilan yang benar-benar dinamis. Siapkan tongkat sihirmu! ✨

🎩🐇✨
State itu seperti topi ajaib: kita bisa mengeluarkan banyak hal darinya!

🔄 1. Mengupdate State Berdasarkan Nilai Sebelumnya

Kadang kita perlu mengubah state menggunakan nilai lamanya. Contoh paling gampang: tombol tambah pada counter. Kalau kita hanya menulis setAngka(angka + 1), itu aman-aman saja. Tapi bagaimana jika ada banyak tombol yang mengubah angka secara bersamaan? Bisa terjadi race condition (keadaan balapan).

React menyediakan cara yang lebih aman: menggunakan fungsi di dalam setter. Fungsi itu akan menerima nilai state sebelumnya, dan kita mengembalikan nilai baru.

import { useState } from 'react'; function CounterAman() { const [angka, setAngka] = useState(0); function tambah() { setAngka(angka + 1); // cara biasa (OK untuk sederhana) } function tambahDuaKali() { // cara aman: pakai fungsi setAngka(prev => prev + 1); setAngka(prev => prev + 1); // akan menambah 2 sekaligus } return ( <div> <h2>Angka: {angka}</h2> <button onClick={tambah}>Tambah 1</button> <button onClick={tambahDuaKali}>Tambah 2</button> </div> ); }

Dengan setAngka(prev => prev + 1), kita bilang ke React: "Ambil nilai terbaru, lalu tambah 1". Ini aman meskipun dipanggil berkali-kali.

Coba klik tombol "Tambah 2", maka angka akan naik 2 setiap kali. Kalau pakai cara biasa, dua baris setAngka(angka + 1) tidak akan bekerja karena angka masih belum berubah di antara dua panggilan.

💡 Aturan praktis: Gunakan fungsi updater (seperti prev => prev + 1) jika state baru bergantung pada state sebelumnya. Untuk hal lain (misal mengisi input), bisa langsung pakai nilai baru.

📦 2. State dengan Objek (Lebih dari Satu Nilai)

Kadang kita ingin menyimpan beberapa informasi dalam satu state. Misalnya data profil: nama, umur, kota. Kita bisa menggunakan objek.

import { useState } from 'react'; function Profil() { const [profil, setProfil] = useState({ nama: '', umur: '', kota: '' }); function handleChange(event) { const { name, value } = event.target; setProfil({ ...profil, // salin semua properti lama [name]: value // perbarui properti yang sesuai }); } return ( <div> <input name="nama" value={profil.nama} onChange={handleChange} placeholder="Nama" /> <input name="umur" value={profil.umur} onChange={handleChange} placeholder="Umur" /> <input name="kota" value={profil.kota} onChange={handleChange} placeholder="Kota" /> <div className="card"> <p><strong>Nama:</strong> {profil.nama || '-'}</p> <p><strong>Umur:</strong> {profil.umur || '-'}</p> <p><strong>Kota:</strong> {profil.kota || '-'}</p> </div> </div> ); }

Penting! Kita tidak boleh mengubah objek langsung, misalnya profil.nama = value. Kita harus membuat objek baru dengan spread operator ...profil lalu menimpa properti yang diubah. Ini karena state harus dianggap immutable (tidak berubah langsung).

⚠️ Jangan lakukan ini:
profil.nama = "Andi"; setProfil(profil); — React tidak akan mendeteksi perubahan karena objeknya sama (referensi sama). Selalu buat objek baru!

📋 3. State dengan Array (Daftar Dinamis)

Sekarang kita buat daftar tugas (todo list) sederhana. Kita akan menambah item ke dalam array.

import { useState } from 'react'; function DaftarTugas() { const [tugas, setTugas] = useState([]); const [inputTugas, setInputTugas] = useState(''); function tambahTugas() { if (inputTugas.trim() === '') return; setTugas([...tugas, inputTugas]); // buat array baru dengan item baru setInputTugas(''); // kosongkan input } function hapusTugas(index) { const tugasBaru = tugas.filter((item, i) => i !== index); setTugas(tugasBaru); } return ( <div> <h3>📝 Daftar Tugas</h3> <input value={inputTugas} onChange={(e) => setInputTugas(e.target.value)} placeholder="Tambah tugas..." /> <button onClick={tambahTugas}>Tambah</button> <ul> {tugas.map((item, index) => ( <li key={index}> {item} <button onClick={() => hapusTugas(index)}>❌</button> </li> ))} </ul> </div> ); }

Perhatikan: untuk menambah item, kita buat array baru dengan [...tugas, inputTugas]. Untuk menghapus, kita gunakan filter yang juga menghasilkan array baru. Ingat: jangan gunakan metode yang mengubah array asli seperti push, pop, splice. Selalu buat array baru.

🎨 Latihan: Aplikasi Biodata Dinamis

Buat komponen Biodata yang memiliki:

  • Input untuk Nama, Umur, Hobi (bisa ditambah lebih dari satu hobi).
  • Untuk hobi, sediakan tombol "Tambah Hobi" yang akan menambahkan input baru untuk hobi (gunakan state array untuk daftar hobi).
  • Tampilkan semua data dalam kartu cantik.
  • Gunakan objek state untuk menyimpan nama dan umur, dan array state untuk hobi.

Kerangka (coba selesaikan sendiri):

import { useState } from 'react'; function Biodata() { const [data, setData] = useState({ nama: '', umur: '' }); const [hobi, setHobi] = useState(['']); // array hobi, awalnya satu hobi kosong function handleDataChange(e) { const { name, value } = e.target; setData({ ...data, [name]: value }); } function handleHobiChange(index, value) { // buat salinan array hobi, lalu ubah satu indeks const hobiBaru = [...hobi]; hobiBaru[index] = value; setHobi(hobiBaru); } function tambahHobi() { setHobi([...hobi, '']); // tambah hobi kosong baru } function hapusHobi(index) { setHobi(hobi.filter((_, i) => i !== index)); } return ( <div> <h3>📋 Biodata Diri</h3> <input name="nama" value={data.nama} onChange={handleDataChange} placeholder="Nama" /> <input name="umur" value={data.umur} onChange={handleDataChange} placeholder="Umur" /> <h4>Hobi:</h4> {hobi.map((h, index) => ( <div key={index}> <input value={h} onChange={(e) => handleHobiChange(index, e.target.value)} placeholder={`Hobi ${index + 1}`} /> <button onClick={() => hapusHobi(index)}>Hapus</button> </div> ))} <button onClick={tambahHobi}>➕ Tambah Hobi</button> <div className="card"> <h4>Data Kamu:</h4> <p><strong>Nama:</strong> {data.nama || '-'}</p> <p><strong>Umur:</strong> {data.umur || '-'}</p> <p><strong>Hobi:</strong></p> <ul> {hobi.map((h, i) => h.trim() !== '' && <li key={i}>{h}</li>)} </ul> </div> </div> ); }

Latihan ini menggabungkan objek state, array state, dan update yang aman. Coba kerjakan, lalu bandingkan dengan kode di atas.

💡 Tips Penting

  • Immutability: Jangan pernah mengubah state secara langsung. Selalu buat nilai baru (objek/array baru).
  • Fungsi updater: Gunakan setX(prev => ...) jika perubahan bergantung pada nilai sebelumnya.
  • State terpisah vs objek: Untuk nilai yang sering berubah bersama, lebih mudah pakai objek. Untuk nilai yang independen, pisahkan saja.
  • Key pada list: Saat merender array dengan map, beri key yang unik (misal index, tapi lebih baik pakai id jika ada).

🚀 Kesimpulan

  • Kita bisa mengupdate state dengan aman menggunakan fungsi updater.
  • State bisa berupa objek atau array untuk data kompleks.
  • Selalu buat salinan baru saat mengupdate objek/array, jangan ubah langsung.
  • Dengan useState kita bisa membuat berbagai interaksi dinamis: counter, form multi-input, daftar tugas, dll.

Di artikel selanjutnya kita akan belajar tentang useEffect – untuk melakukan sesuatu "setelah" komponen selesai render, misalnya mengambil data dari API. Sampai jumpa!

Lebih baru Lebih lama

نموذج الاتصال