Responding to Events
React memungkinkan Anda menambahkan event handlers ke dalam JSX Anda. Event handler adalah fungsi-fungsi yang akan dipicu sebagai respons terhadap interaksi seperti mengeklik, mengarahkan kursor, memfokuskan pada input formulir, dan sebagainya.
You will learn
- Berbagai cara untuk menulis event handler
- Bagaimana cara mengoper logika penanganan event dari komponen induk
- Bagaimana event berpropagasi dan cara menghentikannya
Menambahkan event handlers
Untuk menambahkan event handler, pertama-tama Anda akan mendefinisikan sebuah fungsi dan kemudian mengopernya sebagai prop ke tag JSX yang sesuai. Sebagai contoh, berikut adalah sebuah tombol yang belum melakukan apa pun:
export default function Button() { return ( <button> I don't do anything </button> ); }
Anda dapat membuatnya menampilkan pesan ketika pengguna mengeklik dengan mengikuti tiga langkah berikut:
- Deklarasikan sebuah fungsi bernama
handleClick
di dalam komponenButton
Anda. - Implementasikan logika di dalam fungsi tersebut (gunakan
alert
untuk menampilkan pesan). - Tambahkan
onClick={handleClick}
ke tag JSX<button>
.
export default function Button() { function handleClick() { alert('You clicked me!'); } return ( <button onClick={handleClick}> Click me </button> ); }
Anda mendefinisikan fungsi handleClick
dan kemudian mengopernya sebagai prop ke <button>
. handleClick
adalah sebuah event handler. Fungsi event handler:
- Biasanya didefinisikan di dalam komponen Anda.
- Memiliki nama yang diawali dengan
handle
, diikuti oleh nama event.
Berdasarkan konvensi, adalah hal yang umum untuk memberi nama event handler dengan format handle
diikuti oleh nama event. Anda sering melihat contoh seperti onClick={handleClick}
, onMouseEnter={handleMouseEnter}
, dan lain sebagainya.
Sebagai alternatif, Anda juga dapat mendefinisikan event handler secara inline dalam JSX:
<button onClick={function handleClick() {
alert('You clicked me!');
}}>
Atau, lebih singkatnya, menggunakan arrow function:
<button onClick={() => {
alert('You clicked me!');
}}>
Semua gaya penulisan tersebut equivalen. Inline event handler sangat praktis untuk fungsi-fungsi yang singkat.
Membaca props pada event handler
Karena event handler dideklarasikan di dalam komponen, mereka memiliki akses ke prop komponen tersebut. Ini adalah tombol yang, ketika diklik, menampilkan alert dengan prop message
-nya.
function AlertButton({ message, children }) { return ( <button onClick={() => alert(message)}> {children} </button> ); } export default function Toolbar() { return ( <div> <AlertButton message="Playing!"> Play Movie </AlertButton> <AlertButton message="Uploading!"> Upload Image </AlertButton> </div> ); }
Ini membuat kedua tombol tersebut menampilkan dua pesan yang berbeda. Coba untuk mengganti pesan yang dioper kepada mereka.
Mengoper event handler sebagai prop
Seringnya, Anda akan ingin komponen induk untuk menentukan event handler anaknya. Misalnya pada tombol: bergantung kepada tempat Anda menggunakan komponen Button
, Anda mungkin ingin untuk menjalankan fungsi berbeda—mungkin satu memutar film dan satu lagi menggungah gambar.
Untuk melakukan ini, oper prop yang diterima komponen dari induknya sebagai event handler seperti ini:
function Button({ onClick, children }) { return ( <button onClick={onClick}> {children} </button> ); } function PlayButton({ movieName }) { function handlePlayClick() { alert(`Playing ${movieName}!`); } return ( <Button onClick={handlePlayClick}> Play "{movieName}" </Button> ); } function UploadButton() { return ( <Button onClick={() => alert('Uploading!')}> Upload Image </Button> ); } export default function Toolbar() { return ( <div> <PlayButton movieName="Kiki's Delivery Service" /> <UploadButton /> </div> ); }
Di sini, komponen Toolbar
me-render PlayButton
dan UploadButton
:
PlayButton
mengoperhandlePlayClick
sebagai proponClick
keButton
di dalamnya.UploadButton
mengoper() => alert('Uploading!')
sebagai proponClick
keButton
di dalamnya.
Terakhir, komponen Button
Anda menerima prop bernama onClick
. Itu mengoper prop tersebut secara langsung ke <button>
built-in peramban dengan onClick={onClick}
. Ini memberitahukan React untuk memanggil fungsi yang dioper pada saat klik.
Jika anda menggunakan sebuah design system, adalah hal yang umum untuk komponen seperti tombol untuk mengandung styling tetapi tidak menentukan perilaku. Alih-alih seperti itu, komponen seperti PlayButton
dan UploadButton
akan mengoper event handler ke bawah.
Naming event handler props
Komponen built-in seperti <button>
dan <div>
hanya mendukung nama event peramban (browser event names) seperti onClick
. Namun, ketika Anda membuat komponen Anda sendiri, Anda dapat menamakan prop event handler-nya sesuka Anda.
Berdasarkan konvensi, prop event handler harus diawali dengan on
, diikuti dengan huruf kapital.
Misalnya, prop onClick
milk komponen Button
bisa saja dinamakan onSmash
:
function Button({ onSmash, children }) { return ( <button onClick={onSmash}> {children} </button> ); } export default function App() { return ( <div> <Button onSmash={() => alert('Playing!')}> Play Movie </Button> <Button onSmash={() => alert('Uploading!')}> Upload Image </Button> </div> ); }
Pada contoh ini, <button onClick={onSmash}>
menunjukkan bahwa <button>
(huruf kecil) milik peramban masih membutuhkan prop bernama onClick
, tetapi prop yang diterima oleh komponen kustom Button
dapat diberi nama sesuka Anda!
Saat komponen Anda mendukung beberapa interaksi, Anda mungkin memberi nama prop event handler untuk konsep spesifik terhadap aplikasi. Misalnya, komponen Toolbar
ini menerima event handler onPlayMovie
dan onUploadImage
.
export default function App() { return ( <Toolbar onPlayMovie={() => alert('Playing!')} onUploadImage={() => alert('Uploading!')} /> ); } function Toolbar({ onPlayMovie, onUploadImage }) { return ( <div> <Button onClick={onPlayMovie}> Play Movie </Button> <Button onClick={onUploadImage}> Upload Image </Button> </div> ); } function Button({ onClick, children }) { return ( <button onClick={onClick}> {children} </button> ); }
Perhatikan bagaimana komponen App
tidak perlu tahu apa yang Toolbar
akan lakukan dengan onPlayMovie
atau onUploadImage
. Itu adalah detail implementasi dari Toolbar
. Di sini, Toolbar
mengoper mereka sebagai handler onClick
kepada Button
-nya, tetapi itu bisa saja memicu mereka dengan keyboard shortcut. Memberi nama prop berdasarkan interaksi spesifik aplikasi seperti onPlayMovie
memberikan Anda fleksibilitas untuk mengganti bagaimana mereka digunakan nanti.
Propagasi event
Event handler akan menangkap event dari anak mana pun yang mungkin dimiliki oleh komponen Anda. Kita sebut bahwa sebuah event “menggelembung” (bubbles) atau “berpropagasi” ke atas pohon: mulai dari tempat event terjadi, dan bergerak ke atas di dalam pohon.
<div>
ini mengandung dua tombol. Keduanya, baik <div>
maupun setiap tombol masing-masing memiliki handler onClick
mereka sendiri. Handle mana yang Anda pikir akan dijalankan saat Anda mengeklik sebuah tombol?
export default function Toolbar() { return ( <div className="Toolbar" onClick={() => { alert('You clicked on the toolbar!'); }}> <button onClick={() => alert('Playing!')}> Play Movie </button> <button onClick={() => alert('Uploading!')}> Upload Image </button> </div> ); }
Jika Anda mengeklik salah satu tombol, onClick
milik tombol tersebut akan berjalan terlebih dahulu, diikuti dengan onClick
milik <div>
induknya. Jadi, dua pesan akan muncul. Jika Anda mengeklik toolbar-nya, hanya onClick
milik <div>
induknya yang akan berjalan.
Menghentikan propagasi
Event handlers receive an event object as their only argument. By convention, it’s usually called e
, which stands for “event”. You can use this object to read information about the event.
That event object also lets you stop the propagation. If you want to prevent an event from reaching parent components, you need to call e.stopPropagation()
like this Button
component does:
function Button({ onClick, children }) { return ( <button onClick={e => { e.stopPropagation(); onClick(); }}> {children} </button> ); } export default function Toolbar() { return ( <div className="Toolbar" onClick={() => { alert('You clicked on the toolbar!'); }}> <Button onClick={() => alert('Playing!')}> Play Movie </Button> <Button onClick={() => alert('Uploading!')}> Upload Image </Button> </div> ); }
When you click on a button:
- React calls the
onClick
handler passed to<button>
. - That handler, defined in
Button
, does the following:- Calls
e.stopPropagation()
, preventing the event from bubbling further. - Calls the
onClick
function, which is a prop passed from theToolbar
component.
- Calls
- That function, defined in the
Toolbar
component, displays the button’s own alert. - Since the propagation was stopped, the parent
<div>
’sonClick
handler does not run.
As a result of e.stopPropagation()
, clicking on the buttons now only shows a single alert (from the <button>
) rather than the two of them (from the <button>
and the parent toolbar <div>
). Clicking a button is not the same thing as clicking the surrounding toolbar, so stopping the propagation makes sense for this UI.
Deep Dive
In rare cases, you might need to catch all events on child elements, even if they stopped propagation. For example, maybe you want to log every click to analytics, regardless of the propagation logic. You can do this by adding Capture
at the end of the event name:
<div onClickCapture={() => { /* this runs first */ }}>
<button onClick={e => e.stopPropagation()} />
<button onClick={e => e.stopPropagation()} />
</div>
Each event propagates in three phases:
- It travels down, calling all
onClickCapture
handlers. - It runs the clicked element’s
onClick
handler. - It travels upwards, calling all
onClick
handlers.
Capture events are useful for code like routers or analytics, but you probably won’t use them in app code.
Passing handlers as alternative to propagation
Notice how this click handler runs a line of code and then calls the onClick
prop passed by the parent:
function Button({ onClick, children }) {
return (
<button onClick={e => {
e.stopPropagation();
onClick();
}}>
{children}
</button>
);
}
You could add more code to this handler before calling the parent onClick
event handler, too. This pattern provides an alternative to propagation. It lets the child component handle the event, while also letting the parent component specify some additional behavior. Unlike propagation, it’s not automatic. But the benefit of this pattern is that you can clearly follow the whole chain of code that executes as a result of some event.
If you rely on propagation and it’s difficult to trace which handlers execute and why, try this approach instead.
Preventing default behavior
Some browser events have default behavior associated with them. For example, a <form>
submit event, which happens when a button inside of it is clicked, will reload the whole page by default:
export default function Signup() { return ( <form onSubmit={() => alert('Submitting!')}> <input /> <button>Send</button> </form> ); }
You can call e.preventDefault()
on the event object to stop this from happening:
export default function Signup() { return ( <form onSubmit={e => { e.preventDefault(); alert('Submitting!'); }}> <input /> <button>Send</button> </form> ); }
Don’t confuse e.stopPropagation()
and e.preventDefault()
. They are both useful, but are unrelated:
e.stopPropagation()
stops the event handlers attached to the tags above from firing.e.preventDefault()
prevents the default browser behavior for the few events that have it.
Can event handlers have side effects?
Absolutely! Event handlers are the best place for side effects.
Unlike rendering functions, event handlers don’t need to be pure, so it’s a great place to change something—for example, change an input’s value in response to typing, or change a list in response to a button press. However, in order to change some information, you first need some way to store it. In React, this is done by using state, a component’s memory. You will learn all about it on the next page.
Recap
- You can handle events by passing a function as a prop to an element like
<button>
. - Event handlers must be passed, not called!
onClick={handleClick}
, notonClick={handleClick()}
. - You can define an event handler function separately or inline.
- Event handlers are defined inside a component, so they can access props.
- You can declare an event handler in a parent and pass it as a prop to a child.
- You can define your own event handler props with application-specific names.
- Events propagate upwards. Call
e.stopPropagation()
on the first argument to prevent that. - Events may have unwanted default browser behavior. Call
e.preventDefault()
to prevent that. - Explicitly calling an event handler prop from a child handler is a good alternative to propagation.
Challenge 1 of 2: Fix an event handler
Clicking this button is supposed to switch the page background between white and black. However, nothing happens when you click it. Fix the problem. (Don’t worry about the logic inside handleClick
—that part is fine.)
export default function LightSwitch() { function handleClick() { let bodyStyle = document.body.style; if (bodyStyle.backgroundColor === 'black') { bodyStyle.backgroundColor = 'white'; } else { bodyStyle.backgroundColor = 'black'; } } return ( <button onClick={handleClick()}> Toggle the lights </button> ); }