Handling Events
React handles events with JSX attributes that take functions as values. The syntax is close to HTML’s onclick but with important differences.
Attaching a handler
Section titled “Attaching a handler”Pass a function reference to a camelCase event prop:
function DeleteButton({ onDelete }) { return ( <button onClick={onDelete}>Delete</button> );}Pass the function reference — not a call. onClick={onDelete} is correct. onClick={onDelete()} calls the function immediately when the component renders, which is almost never what you want.
Inline handlers
Section titled “Inline handlers”For short logic, an inline arrow function is fine:
<button onClick={() => setCount(count + 1)}>+</button>The arrow function is a new function on every render, but for simple handlers this does not matter. If performance becomes a concern (in a large list), useCallback (covered in Module 05) prevents recreating the function unnecessarily.
The event object
Section titled “The event object”React passes a SyntheticEvent to every handler. It has the same shape as a native DOM event but works consistently across browsers:
function handleChange(e) { console.log(e.target.value); // the input's current value}
<input onChange={handleChange} />For form submissions, call e.preventDefault() to stop the browser from reloading the page:
function handleSubmit(e) { e.preventDefault(); // process the form}
<form onSubmit={handleSubmit}>Common events in ZeroBudget
Section titled “Common events in ZeroBudget”| Event | Where it is used |
|---|---|
onClick | Delete buttons, nav arrows, toggle buttons |
onChange | All text and number inputs |
onSubmit | Income add form, transaction add form |
onKeyDown | Inline edit fields (save on Enter) |
The inline edit in CategoryCard uses onKeyDown to save when the user presses Enter:
<input value={nameVal} onChange={e => setNameVal(e.target.value)} onKeyDown={e => { if (e.key === 'Enter') saveName(); }} autoFocus/>Passing data to handlers
Section titled “Passing data to handlers”When rendering a list, you often need to pass the item’s ID to a handler. Use an arrow function to close over the value:
{incomeSources.map(src => ( <li key={src.id}> {src.name} <button onClick={() => onDelete(src.id)}>×</button> </li>))}The () => onDelete(src.id) arrow captures the current src.id for each item in the list.
Exercise
Section titled “Exercise”- In
IncomeSection, add a delete button next to each income row that logs the item’sidto the console when clicked. - Add a form submit handler that calls
e.preventDefault(), logs the currentnameandamountstate, then clears both fields. - Add an
onKeyDownhandler to thenameinput that clears the field when Escape is pressed (e.key === 'Escape'). - Confirm all three handlers work as expected in the browser.
- Attach handlers with camelCase event props:
onClick,onChange,onSubmit,onKeyDown. - Pass the function reference, not a call —
onClick={fn}notonClick={fn()}. - The SyntheticEvent object has
e.target.value,e.key,e.preventDefault(), and all the usual DOM event properties. - Use arrow functions in
mapto close over each item’s identity when passing IDs to handlers.