Menambahkan Interaktivitas
Beberapa hal di layar berubah mengikuti masukan dari pengguna. Contohnya, mengeklik sebuah galeri gambar bisa mengganti gambar yang sedang aktif. Di React, data yang berubah seiring waktu disebut state. Anda dapat menambahkan state ke komponen apa pun, dan mengubahnya sesuai kebutuhan. Di bab ini, Anda akan belajar cara menulis komponen yang dapat menangani interaksi, mengubah state yang dimilikinya, dan menampilkan keluaran yang berbeda seiring berjalannya waktu.
Dalam bab ini
- Cara menangani events yang di-mulai oleh pengguna
- Cara membuat komponen “mengingat” informasi dengan menggunakan state
- Cara React memperbaharui UI dalam dua fase
- Mengapa state tidak langsung terbaharui setelah Anda mengubahnya
- Cara mengantrikan beberapa perubahan state
- Cara mengubah object di dalam state
- Cara mengubah array di dalam state
Menanggapi events
React memungkinkan Anda untuk menambakan event handlers ke JSX Anda. Event handlers adalah fungsi milik Anda yang akan dipanggil sebagai respon terhadap interaksi dari pengguna seperti klik, hover, fokus pada masukan form, dan lain-lain.
Komponen bawaan seperti <button>
hanya mendukung event bawaan dari peramban seperti onClick
. Namun, Anda juga dapat membuat komponen Anda sendiri, dan memberikannya prop event handler dengan nama apa pun, spesifik terhadap aplikasi Anda.
export default function App() { return ( <Toolbar onPlayMovie={() => alert('Memutar!')} onUploadImage={() => alert('Uploading!')} /> ); } function Toolbar({ onPlayMovie, onUploadImage }) { return ( <div> <Button onClick={onPlayMovie}> Putar Film </Button> <Button onClick={onUploadImage}> Upload Gambar </Button> </div> ); } function Button({ onClick, children }) { return ( <button onClick={onClick}> {children} </button> ); }
Siap mempelajari topik ini?
Baca Menangani Event untuk mempelajari cara menambahkan event handler.
Baca Lebih LanjutState: Ingatan dari komponen
Komponen sering perlu mengubah apa yang ada di layar sebagai hasil dari sebuah interaksi. Mengetik kedalam form dapat mengubah sebuah kolom masukan, mengeklik “next” pada sebuah carousel gambar mengubah gambar yang sedang ditampilkan, mengeklik “beli” menambahkan sebuah produk kedalam keranjang belanja. Komponen perlu “mengingat” berbagai hal: nilai masukan saat ini, gambar saat ini, keranjang belanja. Di React, jenis ingatan komponen seperti ini disebut state.
Anda dapat menambahkan state kepada komponen dengan menggunakan Hook useState
. Hooks adalah fungsi spesial yang memungkinkan komponen Anda untuk menggunakan fitur-fitur dari React (state adalah salah satu fitur tersebut). Hook useState
memungkinkan Anda mendeklarasikan sebuah variabel state. Fungsi ini menerima state awal dan mengeluarkan sepasang nilai: state saat ini, dan sebuah fungsi state setter yang memungkinkan Anda untuk mengubah state tersebut.
const [index, setIndex] = useState(0);
const [showMore, setShowMore] = useState(false);
Berikut adalah cara galeri gambar menggunakan dan mengubah state saat diklik:
import { useState } from 'react'; import { sculptureList } from './data.js'; export default function Gallery() { const [index, setIndex] = useState(0); const [showMore, setShowMore] = useState(false); const hasNext = index < sculptureList.length - 1; function handleNextClick() { if (hasNext) { setIndex(index + 1); } else { setIndex(0); } } function handleMoreClick() { setShowMore(!showMore); } let sculpture = sculptureList[index]; return ( <> <button onClick={handleNextClick}> Selanjutnya </button> <h2> <i>{sculpture.name} </i> oleh {sculpture.artist} </h2> <h3> ({index + 1} dari {sculptureList.length}) </h3> <button onClick={handleMoreClick}> {showMore ? 'Sembunyikan' : 'Tampilkan'} detail </button> {showMore && <p>{sculpture.description}</p>} <img src={sculpture.url} alt={sculpture.alt} /> </> ); }
Siap mempelajari topik ini?
Baca State: Ingatan dari komponen untuk mempelajari cara untuk mengingat sebuah nilai dan mengubahnya saat berinteraksi.
Baca Lebih LanjutRender dan commit
Sebelum komponen Anda ditampilkan di layar, mereka harus di render oleh React. Memahami langkah-langkah dalam proses ini akan membantu Anda berpikir tentang bagaimana kode Anda akan dijalankan dan menjelaskan perilakunya.
Bayangkan bahwa komponen Anda adalah koki di dapur, menyusun hidangan lezat dari bahan-bahan dasar. Dalam skenario ini, React adalah pelayan yang mengajukan permintaan dari pelanggan dan membawakan pesanan mereka. Proses permintaan dan pelayanan UI ini memiliki tiga langkah:
- Memicu render (mengirimkan pesanan pelanggan ke dapur)
- Me-render komponen (menyiapkan pesanan di dapur)
- Commit ke DOM (menempatkan pesanan di meja)
Ilustrasi oleh Rachel Lee Nabors
Siap mempelajari topik ini?
Baca Render dan Commit untuk mempelajari lifecycle dari perubahan UI.
Baca Lebih LanjutState sebagai snapshot
Tidak seperti variabel Javascript biasa, state di React berperilaku lebih seperti snapshot. Mengubah state tidaklah mengubah variabel state yang Anda miliki sekarang, tetapi akan memicu render ulang. Ini bisa mengejutkan pada awalnya!
console.log(count); // 0
setCount(count + 1); // Meminta render ulang dengan 1
console.log(count); // Masih 0!
Perilaku ini akan membantu Anda menghindari bug yang susah ditemukan. Berikut adalah aplikasi chat sederhana. Coba tebak apa yang terjadi jika Anda menekan “Kirim” terlebih dahulu dan kemudian mengubah penerima menjadi Bob. Nama siapa yang akan muncul di alert
lima detik kemudian?
import { useState } from 'react'; export default function Form() { const [to, setTo] = useState('Alice'); const [message, setMessage] = useState('Halo'); function handleSubmit(e) { e.preventDefault(); setTimeout(() => { alert(`Anda mengatakan ${message} kepada ${to}`); }, 5000); } return ( <form onSubmit={handleSubmit}> <label> Kepada:{' '} <select value={to} onChange={e => setTo(e.target.value)}> <option value="Alice">Alice</option> <option value="Bob">Bob</option> </select> </label> <textarea placeholder="Message" value={message} onChange={e => setMessage(e.target.value)} /> <button type="submit">Kirim</button> </form> ); }
Siap mempelajari topik ini?
Baca State sebagai Snapshot untuk mempelajari mengapa state terlihat “tetap” dan tak berubah di dalam event handler.
Baca Lebih LanjutMengantrikan serangkaian perubahan state
Komponent ini memiliki bug: mengeklik “+3” hanya akan menambahkan skor satu kali saja.
import { useState } from 'react'; export default function Counter() { const [score, setScore] = useState(0); function increment() { setScore(score + 1); } return ( <> <button onClick={() => increment()}>+1</button> <button onClick={() => { increment(); increment(); increment(); }}>+3</button> <h1>Skor: {score}</h1> </> ) }
State sebagai Snapshot menjelaskan mengapa ini terjadi. Mengubah state akan meminta render ulang baru, tetapi tidak akan mengubah state-nya di kode yang sudah berjalan. Jadi score
tetap 0
setelah Anda memanggil setScore(score + 1)
.
console.log(score); // 0
setScore(score + 1); // setScore(0 + 1);
console.log(score); // 0
setScore(score + 1); // setScore(0 + 1);
console.log(score); // 0
setScore(score + 1); // setScore(0 + 1);
console.log(score); // 0
Anda bisa memperbaiki ini dengan memberikan updater function ketika mengubah state. Perhatikan bagaimana mengganti setScore(score + 1)
dengan setScore(s => s + 1)
memperbaiki tombol “+3”. Cara ini memungkinkan Anda untuk mengantrikan beberapa perubahan state.
import { useState } from 'react'; export default function Counter() { const [score, setScore] = useState(0); function increment() { setScore(s => s + 1); } return ( <> <button onClick={() => increment()}>+1</button> <button onClick={() => { increment(); increment(); increment(); }}>+3</button> <h1>Skor: {score}</h1> </> ) }
Siap mempelajari topik ini?
Baca Mengantrikan serangkaian perubahan state untuk mempelajari cara meng-queue sebuah rentetan perubahan state.
Baca Lebih LanjutMengubah object di dalam state
State bisa memegang berbagai jenis nilai JavaScript, termasuk object. Tetapi Anda tidak boleh mengubah object dan array yang Anda simpan di dalam state React secara langsung. Melainkan, ketika Anda ingin mengubah object dan array, Anda perlu membuat object yang baru (atau membuat salinan dari object yang sudah ada), dan kemudian mengubah state-nya untuk menggunakan salinan tersebut.
Biasanya, Anda akan menggunakan sintaks spreads ...
untuk menyalin object dan array yang ingin Anda ubah. Contohnya, mengubah object yang bersarang bisa terlihat seperti ini:
import { useState } from 'react'; export default function Form() { const [person, setPerson] = useState({ name: 'Niki de Saint Phalle', artwork: { title: 'Blue Nana', city: 'Hamburg', image: 'https://i.imgur.com/Sd1AgUOm.jpg', } }); function handleNameChange(e) { setPerson({ ...person, name: e.target.value }); } function handleTitleChange(e) { setPerson({ ...person, artwork: { ...person.artwork, title: e.target.value } }); } function handleCityChange(e) { setPerson({ ...person, artwork: { ...person.artwork, city: e.target.value } }); } function handleImageChange(e) { setPerson({ ...person, artwork: { ...person.artwork, image: e.target.value } }); } return ( <> <label> Nama: <input value={person.name} onChange={handleNameChange} /> </label> <label> Judul: <input value={person.artwork.title} onChange={handleTitleChange} /> </label> <label> Kota: <input value={person.artwork.city} onChange={handleCityChange} /> </label> <label> Gambar: <input value={person.artwork.image} onChange={handleImageChange} /> </label> <p> <i>{person.artwork.title}</i> {' oleh '} {person.name} <br /> (berada di {person.artwork.city}) </p> <img src={person.artwork.image} alt={person.artwork.title} /> </> ); }
Jika menyalin object di kode terasa terlalu ribet, Anda bisa menggunakan library seperti Immer untuk mengurangi kode yang berulang-ulang:
import { useImmer } from 'use-immer'; export default function Form() { const [person, updatePerson] = useImmer({ name: 'Niki de Saint Phalle', artwork: { title: 'Blue Nana', city: 'Hamburg', image: 'https://i.imgur.com/Sd1AgUOm.jpg', } }); function handleNameChange(e) { updatePerson(draft => { draft.name = e.target.value; }); } function handleTitleChange(e) { updatePerson(draft => { draft.artwork.title = e.target.value; }); } function handleCityChange(e) { updatePerson(draft => { draft.artwork.city = e.target.value; }); } function handleImageChange(e) { updatePerson(draft => { draft.artwork.image = e.target.value; }); } return ( <> <label> Nama: <input value={person.name} onChange={handleNameChange} /> </label> <label> Judul: <input value={person.artwork.title} onChange={handleTitleChange} /> </label> <label> Kota: <input value={person.artwork.city} onChange={handleCityChange} /> </label> <label> Gambar: <input value={person.artwork.image} onChange={handleImageChange} /> </label> <p> <i>{person.artwork.title}</i> {' oleh '} {person.name} <br /> (located in {person.artwork.city}) </p> <img src={person.artwork.image} alt={person.artwork.title} /> </> ); }
Siap mempelajari topik ini?
Baca Mengubah Object di dalam State untuk mempelajari cara mengubah object dengan benar.
Baca Lebih LanjutMengubah array di dalam state
Array adalah tipe object lain yang bersifat mutable di Javascript yang bisa Anda simpan di state dan harus diperlakukan sebagai read-only. Seperti object, ketika Anda ingin mengubah array yang disimpan di state, Anda perlu membuat array baru (atau membuat salinan dari array yang sudah ada), dan kemudian mengubah state-nya untuk menggunakan array baru tersebut:
import { useState } from 'react'; let nextId = 3; const initialList = [ { id: 0, title: 'Big Bellies', seen: false }, { id: 1, title: 'Lunar Landscape', seen: false }, { id: 2, title: 'Terracotta Army', seen: true }, ]; export default function BucketList() { const [list, setList] = useState( initialList ); function handleToggle(artworkId, nextSeen) { setList(list.map(artwork => { if (artwork.id === artworkId) { return { ...artwork, seen: nextSeen }; } else { return artwork; } })); } return ( <> <h1>Bucket List Seni</h1> <h2>Daftar seni untuk dilihat:</h2> <ItemList artworks={list} onToggle={handleToggle} /> </> ); } function ItemList({ artworks, onToggle }) { return ( <ul> {artworks.map(artwork => ( <li key={artwork.id}> <label> <input type="checkbox" checked={artwork.seen} onChange={e => { onToggle( artwork.id, e.target.checked ); }} /> {artwork.title} </label> </li> ))} </ul> ); }
Jika menyalin array di kode terasa terlalu ribet, Anda bisa menggunakan library seperti Immer untuk mengurangi kode yang berulang-ulang:
import { useState } from 'react'; import { useImmer } from 'use-immer'; let nextId = 3; const initialList = [ { id: 0, title: 'Big Bellies', seen: false }, { id: 1, title: 'Lunar Landscape', seen: false }, { id: 2, title: 'Terracotta Army', seen: true }, ]; export default function BucketList() { const [list, updateList] = useImmer(initialList); function handleToggle(artworkId, nextSeen) { updateList(draft => { const artwork = draft.find(a => a.id === artworkId ); artwork.seen = nextSeen; }); } return ( <> <h1>Bucket List Seni</h1> <h2>Daftar seni untuk dilihat:</h2> <ItemList artworks={list} onToggle={handleToggle} /> </> ); } function ItemList({ artworks, onToggle }) { return ( <ul> {artworks.map(artwork => ( <li key={artwork.id}> <label> <input type="checkbox" checked={artwork.seen} onChange={e => { onToggle( artwork.id, e.target.checked ); }} /> {artwork.title} </label> </li> ))} </ul> ); }
Siap mempelajari topik ini?
Baca Mengubah Array di dalam State untuk mempelajari cara mengubah array dengan benar.
Baca Lebih LanjutApa selanjutnya?
Lanjutkan ke Menanggapi Event untuk mulai membaca bab ini halaman demi halaman!
Atau, jika Anda sudah akrab dengan topik ini, mengapa tidak membaca tentang Mengatur State?