Responding to events
Handle clicks, typing, and form submits in React: write event handlers, pass them as props, and control bubbling with stopPropagation and preventDefault.
- 01Write and pass event handlersGive an element a function to run on a click or a key, and pass handlers down to your own components with on… props.
- 02Follow an event through the treeSay which handlers run for a click, in what order, and stop the click going up with stopPropagation.
- 03Stop the browser's own actionKeep a form from loading a new page with preventDefault, and tell it apart from stopPropagation.
A button that answers a click
So far, your components have described what the screen shows. Now they start to answer the person using it. When someone clicks, types, or sends a form, the browser fires an event. In plain JavaScript you'd find the element and call addEventListener. In React you give the element a function, as a prop, in your JSX:
export default function AddToCart() { function handleClick() { console.log("Added Masala chai to the cart"); } return <button onClick={handleClick}>Add to cart</button>;}Lines 2 to 4 define handleClick, a function inside the component. Line 6 hands it to the button as onClick. React keeps it, and calls it each time the button is clicked. A function like this, which runs when an event happens, is an event handler. Click Add to cart in the result: the console under it says Added Masala chai to the cart, once per click.
The prop's name says which event: onClick for clicks, onChange for typing, onSubmit for forms. They're written in camelCase, like the other JSX attributes in Writing markup with JSX. The examples print to the console instead of changing the screen, because changing the screen needs state, the next lesson's topic. A handler is where that change will happen.
When you install a doorbell, you connect it to the button and leave. It rings later, each time someone presses, not while you're wiring it.
- In real life: Wiring a bell to the button by the door
- In React:
onClick={handleClick}on the<button> - In real life: The bell itself, waiting on the wall
- In React: The
handleClickfunction, kept by React - In real life: A visitor pressing the button
- In React: A click, which the browser reports as an event
- In real life: The bell rings, each press, never while you install it
- In React: React calls
handleClickon each click, never while rendering
An event handler is a function you give React to run when something happens. Pass it to a built-in element with a prop like onClick. React calls it with an event object. Then the event goes on to the handlers of the elements around it, unless you stop it.
Pass the function, don't call it
The braces in onClick={handleClick} hold a JavaScript value, and that value must be a function. Add two brackets, and it isn't one any more. Here is the overview's button with that one change, a bug on purpose:
export default function AddToCart() { function handleClick() { console.log("Added Masala chai to the cart"); } return <button onClick={handleClick()}>Add to cart</button>;}Line 6 now says handleClick(). JavaScript inside braces runs right away, so handleClick runs while React renders the button, and onClick gets what it returns: undefined. Look at the console: the message is already there before any click, with a 2 beside it because it ran twice. (Twice because the preview renders in StrictMode, which calls each component two times in development, as Keeping components pure showed.) Click the button: nothing more is printed. Press Edit & run and delete the (): the console starts empty, and each click adds a line.
onClick={handleClick} hands React the function, and React calls it on each click. Right: the brackets in handleClick() run it while React renders, and onClick gets what it returns, undefined.When the handler needs something passed in, wrap the call in an arrow function instead. The arrow function is what onClick gets, and it makes the call later, on the click:
function addToCart(tea, cups) { console.log(`Added ${cups} x ${tea}`);} function TeaButton({ tea }) { function handleClick() { addToCart(tea, 1); } return ( <p> <button onClick={handleClick}>Add {tea}</button> <button onClick={() => addToCart(tea, 2)}>Add 2</button> </p> );} export default function Menu() { return ( <> <TeaButton tea="Masala chai" /> <TeaButton tea="Matcha" /> </> );}- Lines 1 to 3 are an ordinary function,
addToCart, that takes a tea and a number of cups. - Lines 6 to 8 define
handleClickinsideTeaButton. Because it's inside, it can read theteaprop: eachTeaButtongets its ownhandleClick, with its own tea. That's a closure. - Line 12 passes
handleClickby name. Line 13 writes the handler inline, as an arrow function:() => addToCart(tea, 2). Both are functions, so both wait for a click. - Lines 21 and 22 use the component twice, so there are four buttons.
Click Add Matcha: the console says Added 1 x Matcha. Click the Add 2 next to it: Added 2 x Matcha. Writing onClick={addToCart(tea, 2)} without the arrow would run while rendering, like the bug above. A named handler and an inline arrow work the same; inline is handy for one short call.
Passing handlers down as props
A handler is a function, and a function can be a prop, as Passing props to a component showed. That lets one component decide what happens, and another decide how it looks. Here, Button draws every button in a profile toolbar the same way, and each parent gives it a different job:
function Button({ onClick, children }) { return ( <button className="button" onClick={onClick}> {children} </button> );} function SaveButton() { function handleSaveClick() { console.log("Saved your profile"); } return <Button onClick={handleSaveClick}>Save</Button>;} function ResetButton() { return <Button onClick={() => console.log("Put back your old details")}>Reset</Button>;} export default function ProfileToolbar() { return ( <div className="toolbar"> <SaveButton /> <ResetButton /> </div> );}Button (lines 1 to 7) takes an onClick prop and passes it to the real <button> on line 3. SaveButton passes its own handleSaveClick on line 14, and ResetButton passes an inline arrow function on line 18. Click Save, then Reset: each prints its own line. Design systems work this way: their buttons bring the look, and your code brings the behavior.
Naming: on for props, handle for functions
Built-in elements like <button> only take React's event names, spelled exactly: onClick, onChange. Your own components can take any prop names. By habit, a prop that takes a handler starts with on and a capital letter, and the function you pass starts with handle. Name props after what happens in your app, not after the mouse:
function ProductCard({ name, price, onShowDetails, onAddToCart }) { return ( <article className="card"> <h3>{name}</h3> <p>${price}</p> <button onClick={onShowDetails}>Details</button> <button onClick={() => onAddToCart(name)}>Add to cart</button> </article> );} export default function Shop() { function handleAddToCart(name) { console.log(`${name} added to the cart`); } return ( <div className="shelf"> <ProductCard name="Matcha" price={6} onShowDetails={() => console.log("Showing Matcha's details")} onAddToCart={handleAddToCart} /> <ProductCard name="Sencha" price={4} onShowDetails={() => console.log("Showing Sencha's details")} onAddToCart={handleAddToCart} /> </div> );}ProductCard takes onShowDetails and onAddToCart on line 1. Line 6 passes onShowDetails straight to the Details button. Line 7 wraps onAddToCart in an arrow function, so it can pass the card's name. Shop defines handleAddToCart once, on lines 13 to 15, and gives it to both cards on lines 23 and 29. Click Add to cart on Sencha: Sencha added to the cart. Shop doesn't know a button was involved; later, the same prop could be called from a keyboard shortcut.
Shop passes its function down as onAddToCart, and ProductCard passes a function to the <button>. A click runs that function, which calls Shop's handleAddToCart: the click comes back up to where the function was written.How to read the table: each row is a name you might write, what it is, and whether React runs it on its own.
| When you write | What it is | Does React run it on a click? |
|---|---|---|
onClick on <button> | A browser event that React knows. | Yes. Built-in elements only take React's event names, spelled exactly. |
onAddToCart on <ProductCard> | A prop of your own component. | Only if ProductCard passes it to an element, or calls it. |
onClick on <ProductCard> | Also just a prop, with a familiar name. | Only if ProductCard uses it. React doesn't attach it for you. |
handleAddToCart | A function that handles something, by the usual naming. | It's the function you pass, not a prop name. |
One more habit from React's documentation: put click handlers on elements made for clicking, like <button>. A <div onClick> works with a mouse, but it can't be reached with the Tab key or pressed with Enter, so people who use a keyboard can't use it.
The event object React passes in
React calls your handler with one argument: an event object describing what happened. By habit it's called e. It has the properties you know from the browser's events, like type and target:
export default function SearchBox() { function handleClick(e) { console.log(e.type, "on", e.target.tagName, "handled by", e.currentTarget.tagName); } function handleChange(e) { console.log("Search for:", e.target.value); } return ( <div onClick={handleClick}> <input placeholder="Search teas" onChange={handleChange} /> <button>Search</button> </div> );}Line 11 puts handleClick on the <div>, not on the button. Click Search: the console says click on BUTTON handled by DIV. e.target is the element that was clicked, and e.currentTarget is the element whose handler is running. They differ because the click travelled from the button to the <div>, which the next section explains. Now type chai in the box: line 7 prints Search for: c, then ch, then cha, then chai. In React, onChange runs on every change to an input's value, on each key, unlike the browser's own change event, which waits until you leave the box. (Clicking into the box prints a click line too: the box is inside the <div>.)
e isn't the browser's event itself. React gives you its own event object, sometimes called a synthetic event. It follows the same standard as the browser's, with the same properties and methods, and fixes a few differences between browsers. The browser's original is inside it, as e.nativeEvent:
export default function Inspect() { function handleClick(e) { console.log("React's event:", e.type); console.log("The browser's event inside it:", e.nativeEvent.type); console.log("The same object?", e === e.nativeEvent); } return <button onClick={handleClick}>Inspect this click</button>;}Click the button. Lines 3 and 4 both say click, and line 5 says The same object? false: two objects, one inside the other. You'll rarely need nativeEvent. One more thing React does for you: it doesn't add a listener to each element. When your app starts, it adds its own listeners, for every kind of event, to the root element your app is rendered into. When an event reaches the root, React works out which of your handlers to call. That's why the next section can talk about a click travelling through your components. A later lesson, React's event system, opens it up.
How to read the table: each row is an event prop you'll use often, what it reports, and whether it goes up to the elements around it.
| Prop | What runs it | Goes up to the elements around it? |
|---|---|---|
onClick | A click, or Enter and Space on a focused button. | Yes. |
onChange on an input | Every change to its value, on each key you type. | Yes. |
onSubmit on a form | The form being sent, by a button or Enter. | Yes. |
onKeyDown | A key being pressed, with e.key naming it. | Yes. |
onFocus and onBlur | An element getting or losing focus. | Yes, in React. |
onMouseEnter and onMouseLeave | The pointer moving onto or off an element. | No. Each runs only on its own element. |
onScroll | The element being scrolled. | No. It runs only on the element you attach it to. |
A click bubbles up the tree
A click on a button is also a click on everything around it. So after the button's onClick, React runs the onClick of each element around it, from the inside out. That's called bubbling, or propagation, the same idea as in the browser's bubbling and capturing. On a product card it causes a bug:
export default function ProductCard() { return ( <article className="card" onClick={() => console.log("card: open Matcha's page")}> <h3>Matcha</h3> <button onClick={() => console.log("button: add Matcha to the cart")}> Add to cart </button> </article> );}Line 3 makes the whole card open the product's page. Line 5 gives the button its own job, adding to the cart. Click Add to cart. The console shows two lines: button: add Matcha to the cart, then card: open Matcha's page. The click bubbled from the button up to the card. Click the word Matcha instead: only the card's line, because nothing inside the card was clicked.
To stop it, call e.stopPropagation() in the button's handler. It tells React not to run the handlers above:
export default function ProductCard() { return ( <article className="card" onClick={() => console.log("card: open Matcha's page")}> <h3>Matcha</h3> <button onClick={(e) => { e.stopPropagation(); console.log("button: add Matcha to the cart"); }} > Add to cart </button> </article> );}Line 6 now takes the event, e, and line 7 stops it before line 8 prints. Click Add to cart: one line, from the button. The card's handler never runs. The heading still opens the card.
Bubbling is the second half of the trip. Before it, the event travels down from the outside to what you clicked, and React runs any capture handlers on the way: the same name with Capture at the end, like onClickCapture.
onClickCapture on the way down, from the outside in. Then it runs onClick on the button and on each element around it, from the inside out: the click bubbles up.Try it, with real React handlers on this page:
export default function ShopPage() {
return (
<section
onClickCapture={() => console.log("section onClickCapture")}
onClick={() => console.log("section onClick")}
>
Shop page
<article
onClickCapture={() => console.log("article onClickCapture")}
onClick={() => console.log("article onClick")}
>
Matcha card
<button
onClickCapture={() => console.log("button onClickCapture")}
onClick={() => console.log("button onClick")}
>
Add to cart
</button>
</article>
</section>
);
}click insideClick Add to cart in the result, then the card around it, then the page around the card. Switch the handlers on and off between clicks.
With capture on, a click on Add to cart runs six handlers: section onClickCapture, article onClickCapture, button onClickCapture on the way down, then button onClick, article onClick, section onClick on the way up. Click the card itself, and only four run: the button isn't on the path. Switch on stopPropagation() in the button and click the button again: four, because the capture handlers had already run when the button stopped the click.
That last point is what capture handlers are for. A stopped click is still seen by every capture handler above it, so they suit code that must see every click, like analytics:
export default function Shop() { function handleAnyClick(e) { console.log("analytics:", e.target.tagName, "clicked"); } return ( <main onClickCapture={handleAnyClick}> <article className="card" onClick={() => console.log("card: open Matcha's page")}> <h3>Matcha</h3> <button onClick={(e) => { e.stopPropagation(); console.log("button: add Matcha to the cart"); }} > Add to cart </button> </article> </main> );}Line 7 puts handleAnyClick on <main> as onClickCapture. Click Add to cart: the console says analytics: BUTTON clicked first, then the button's line. The button's stopPropagation on line 12 still keeps the card from opening, but it can't take back a capture handler that ran before it. You'll rarely write capture handlers in app code; routers and analytics tools use them.
Bubbling is automatic, which makes it easy to forget. If it gets hard to tell which handlers run, there's a plainer way: the child handles its own click and then calls the prop from its parent, as the inbox below does. Every step is then in the code you read.
preventDefault: stop the browser's own action
Some events come with something the browser does by itself, its default action. A link loads its address. A form's submit event sends the form and loads a new page. In a React app, that new page throws away your app. e.preventDefault() cancels the default action:
export default function NewsletterForm() { function handleSubmit(e) { e.preventDefault(); const data = new FormData(e.currentTarget); console.log("Subscribing", data.get("email")); } return ( <form onSubmit={handleSubmit}> <input name="email" aria-label="Email" defaultValue="asha@example.com" /> <button>Subscribe</button> </form> );}Line 9 handles onSubmit on the form, not onClick on the button, so pressing Enter in the box submits too. Line 3 cancels the browser's own submit. Line 4 reads every named field with FormData, and line 5 prints the email. Press Subscribe: the console says Subscribing asha@example.com. Now press Edit & run, delete line 3, and press Subscribe again. Your line still prints, followed by a note from the preview: it caught the submit itself. On a real page nothing would catch it, and the browser would send the form and load a new page, throwing your app away.
The same works for a link that should do something else instead of loading its address:
export default function MiniCart() { function handleClick(e) { e.preventDefault(); console.log("Opening the cart drawer instead of the cart page"); } return ( <a href="/cart" onClick={handleClick}> View cart </a> );}Line 3 keeps the browser from going to /cart, so line 4 can open a drawer instead. Click View cart: the page stays, and the console prints the line. Delete line 3 with Edit & run and click again: now the link tries to load /cart, and the preview says it has no such page.
e.stopPropagation() keeps the event from reaching the handlers above, in your components. e.preventDefault() keeps the browser from doing its own thing, like loading a new page for a form. One doesn't do the other's job.Don't mix up the two. e.stopPropagation() stops the handlers above, in your components, from running. e.preventDefault() stops the browser's own action. A form that reloads needs preventDefault; a card that opens when you meant the button needs stopPropagation.
React 19 adds another way for forms: pass a function as the form's action, like <form action={subscribe}>, and React handles the submit for you, without a reload. The Form actions lesson teaches them. onSubmit with preventDefault keeps working, and you'll see it in most code written before React 19.
An inbox with three kinds of click
Here everything comes together in one small app: rows that open, a button inside each row that shouldn't, and a form that mustn't reload.
const messages = [ { id: 1, from: "Asha", subject: "Your order has shipped" }, { id: 2, from: "Ravi", subject: "Matcha is back in stock" },]; function MessageRow({ message, onOpen, onArchive }) { function handleArchiveClick(e) { e.stopPropagation(); onArchive(message.id); } return ( <li className="row" onClick={() => onOpen(message.id)}> <strong>{message.from}</strong> <span>{message.subject}</span> <button onClick={handleArchiveClick}>Archive</button> </li> );} function ReplyForm({ onSend }) { function handleSubmit(e) { e.preventDefault(); onSend(new FormData(e.currentTarget).get("reply")); } return ( <form onSubmit={handleSubmit}> <input name="reply" aria-label="Reply" defaultValue="Thanks!" /> <button>Send</button> </form> );} export default function Inbox() { return ( <> <ul className="inbox"> {messages.map((message) => ( <MessageRow key={message.id} message={message} onOpen={(id) => console.log("Open message", id)} onArchive={(id) => console.log("Archive message", id)} /> ))} </ul> <ReplyForm onSend={(text) => console.log("Send reply:", text)} /> </> );}- Rows open on a click: line 13 calls
onOpenwith the message's id. Click Asha's subject:Open message 1. - Archive doesn't open the row:
handleArchiveClickon lines 7 to 10 stops the click, then calls the parent'sonArchiveitself. Click Archive on Ravi's row: onlyArchive message 2. - The reply form doesn't reload: line 23 cancels the submit, and line 24 hands the text to
onSend. Press Send:Send reply: Thanks!. - Inbox decides what each one does: lines 43, 44, and 48 pass the handlers down. The rows and the form only report what happened.
A real inbox would also let keyboard users open a row, for example by making the subject a button or a link. The row's onClick here keeps the example small.
Sort each problem by the call that fixes it:
- A newsletter form reloads the page when you press Subscribe
- Clicking Delete on a note also opens the note
- A View cart link should open a drawer, not go to /cart
- A click inside a modal also closes it, through the backdrop's onClick
- Ticking a row's checkbox also selects the whole row
- Right-click should show your own menu, not the browser's
- Space in your game scrolls the page down
- Add to cart on a product card also opens the product page
Drag each problem, or pick it and choose a group. Ask: is a handler above running when it shouldn't, or is the browser doing something by itself?
Common mix-ups
- "
onClick={handleClick()}runs handleClick on a click." No. The brackets call it while React renders. PasshandleClick, or an arrow function. - "Any component understands onClick." No. Only built-in elements like
<button>do. On your own component,onClickis just a prop until the component passes it to an element. - "stopPropagation stops a form from reloading." No. That's
preventDefault.stopPropagationonly stops the handlers above. - "Only the clicked element's onClick runs." No. The click bubbles: every
onClickaround it runs too, unless one stops it. - "Returning false from a handler cancels the default." No. React ignores what a handler returns. Call
e.preventDefault(). - "e is the browser's event." Not quite. It's React's event object, with the browser's inside, in
e.nativeEvent.
Practice exercises
Each exercise shows its result under the code, or when you press Edit & run, and its console under the result. For the first one, answer before you run anything.
You click Track once. What does the console show, in order?
export default function OrderRow() {
return (
<div onClick={() => console.log("row")}>
Order #12
<button onClick={() => console.log("button")}>Track</button>
</div>
);
}export default function OrderRow() { return ( <div onClick={() => console.log("row")}> Order #12 <button onClick={() => console.log("button")}>Track</button> </div> );}The button's handler prints button. The click then bubbles to the <div>, whose handler prints row. So: button, then row.
The console says You liked Matcha before anyone clicks, and clicking does nothing. Fix it. Then: before you click, how many lines does the console show?
export default function LikeButton() { function handleLike() { console.log("You liked Matcha"); } return <button onClick={handleLike()}>Like</button>;}export default function LikeButton() { function handleLike() { console.log("You liked Matcha"); } return <button onClick={handleLike}>Like</button>;}onClick={handleLike()} ran handleLike while rendering, twice in StrictMode, and gave onClick undefined. onClick={handleLike} passes the function, so the console starts empty and each click prints one line.
The + and - buttons do nothing. Connect them to the handlers CartLine passes in. What does the console show after + twice?
function QuantityStepper({ quantity, onIncrease, onDecrease }) { return ( <p> <button>-</button> {quantity} <button>+</button> </p> );} export default function CartLine() { return ( <QuantityStepper quantity={2} onIncrease={() => console.log("Quantity + 1")} onDecrease={() => console.log("Quantity - 1")} /> );}function QuantityStepper({ quantity, onIncrease, onDecrease }) { return ( <p> <button onClick={onDecrease}>-</button> {quantity} <button onClick={onIncrease}>+</button> </p> );} export default function CartLine() { return ( <QuantityStepper quantity={2} onIncrease={() => console.log("Quantity + 1")} onDecrease={() => console.log("Quantity - 1")} /> );}A prop only does something when the component uses it. Passing onIncrease to the + button's onClick means a click calls the function CartLine wrote.
Clicking Delete deletes the note and also opens it. Make Delete only delete. What does one click print now?
function NoteRow({ note, onOpen, onDelete }) { return ( <li onClick={() => onOpen(note.id)}> {note.title} <button onClick={() => onDelete(note.id)}>Delete</button> </li> );} export default function Notes() { return ( <ul> <NoteRow note={{ id: 7, title: "Order more matcha" }} onOpen={(id) => console.log("Open note", id)} onDelete={(id) => console.log("Delete note", id)} /> </ul> );}function NoteRow({ note, onOpen, onDelete }) { return ( <li onClick={() => onOpen(note.id)}> {note.title}{" "} <button onClick={(e) => { e.stopPropagation(); onDelete(note.id); }} > Delete </button> </li> );} export default function Notes() { return ( <ul> <NoteRow note={{ id: 7, title: "Order more matcha" }} onOpen={(id) => console.log("Open note", id)} onDelete={(id) => console.log("Delete note", id)} /> </ul> );}The click bubbled from the button to the <li>, which opened the note. e.stopPropagation() in the button keeps it from going up, and the button still calls onDelete.
On a real page, pressing Search would load a new page and throw the app away; here the preview catches the submit and says so in the console. Handle it yourself, so the preview has nothing to catch. Which call did you add?
export default function TeaSearch() { function handleSubmit(e) { console.log("Searching for", e.currentTarget.elements.query.value); } return ( <form onSubmit={handleSubmit}> <input name="query" aria-label="Search" defaultValue="chai" /> <button>Search</button> </form> );}export default function TeaSearch() { function handleSubmit(e) { e.preventDefault(); console.log("Searching for", e.currentTarget.elements.query.value); } return ( <form onSubmit={handleSubmit}> <input name="query" aria-label="Search" defaultValue="chai" /> <button>Search</button> </form> );}Submitting a form makes the browser send it and load a new page. e.preventDefault() cancels that, so the app stays, and the preview has nothing left to catch: the console shows only Searching for chai.
The toolbar should print clicked: Bold or clicked: Delete for every click, but Delete stops the click. Make the toolbar hear it anyway. What's the first line when you click Delete?
export default function EditorToolbar() { return ( <div onClick={() => console.log("toolbar clicked")}> <button onClick={() => console.log("Bold")}>Bold</button> <button onClick={(e) => { e.stopPropagation(); console.log("Delete"); }} > Delete </button> </div> );}export default function EditorToolbar() { return ( <div onClickCapture={(e) => console.log("clicked:", e.target.textContent)}> <button onClick={() => console.log("Bold")}>Bold</button> <button onClick={(e) => { e.stopPropagation(); console.log("Delete"); }} > Delete </button> </div> );}A capture handler runs on the way down, before the button's onClick can stop anything, so it sees both buttons' clicks. It prints first, then the button's own line.
Check your understanding
For order questions, trace the click: capture handlers from the outside in, then onClick from the clicked element out. Press Edit & run on a question's code and click to check.
Question 1 of 7What happens when you click Save?
Read the code, then predictexport default function SaveButton() { function save() { console.log("Saved"); } return <button onClick={save()}>Save</button>; }Choose an answer to see the explanation.
Question 2 of 7In what order does the console show the letters after one click on Go?
Read the code, then predictexport default function Page() { return ( <section onClickCapture={() => console.log("A")}> <div onClick={() => console.log("B")}> <button onClick={() => console.log("C")}>Go</button> </div> </section> ); }Choose an answer to see the explanation.
Question 3 of 7A newsletter form reloads the page on submit, and your log disappears. Which call stops that?
Choose an answer to see the explanation.
Question 4 of 7
ProductCardis your own component, and it never reads a prop calledonClick. What does<ProductCard onClick={handleClick} />do on a click?Choose an answer to see the explanation.
Question 5 of 7What does this List print when you click Remove?
Read the code, then predictexport default function List() { function handleClick(e) { console.log(e.currentTarget.tagName); } return ( <ul onClick={handleClick}> <li> <button>Remove</button> </li> </ul> ); }Choose an answer to see the explanation.
Question 6 of 7Which of these React events doesn't go up to the handlers of the elements around it?
Choose an answer to see the explanation.
Question 7 of 7What is the
ethat React passes to your handler?Choose an answer to see the explanation.
Key takeaways
- An event handler is a function you pass to a built-in element with a prop like
onClick. React calls it when the event happens. - Pass the function,
onClick={handleClick}, or an arrow function.handleClick()runs while rendering. - Handlers can be props. Name the props
onSomething, after what happens in your app, and the functionshandleSomething. - React passes an event object,
e: its own, with the browser's ine.nativeEvent.e.targetis what was clicked;e.currentTargetis where the handler sits. - Events go down through
onClickCapturehandlers, then bubble up throughonClickhandlers.e.stopPropagation()stops the trip up.onScrolldoesn't bubble. e.preventDefault()stops the browser's own action, like a form loading a new page.
Remember the one-liner.
Give React a function, not a call; the event bubbles up unless you stop it, and the browser acts unless you prevent it.
Coming next: State: a component's memory. Your handlers will finally change what's on the screen.