Handling Events
Responding to clicks, keys, and input in JSX, and the differences from plain DOM listeners.
Attaching a handler
Event props are camelCase and take a function, not a string.
function SaveButton() {
function handleClick() {
console.log("Saved");
}
return <button onClick={handleClick}>Save changes</button>;
}Pass the function, do not call it. onClick={handleClick} is correct; onClick={handleClick()} runs it immediately during render and passes the return value.
Passing arguments
When a handler needs an argument, wrap it in an arrow function so the call happens at click time.
function ItemList({ items, onDelete }) {
return (
<ul>
{items.map(item => (
<li key={item.id}>
{item.label}
<button onClick={() => onDelete(item.id)}>Delete</button>
</li>
))}
</ul>
);
}The event object
React passes a synthetic event to your handler. It wraps the native browser event and normalises behaviour across browsers, with the same interface you already know.
function Form() {
function handleSubmit(event) {
event.preventDefault();
console.log("Submitted without reloading");
}
function handleKeyDown(event) {
if (event.key === "Escape") {
console.log("Cancelled");
}
}
return (
<form onSubmit={handleSubmit}>
<input onKeyDown={handleKeyDown} />
<button type="submit">Send</button>
</form>
);
}preventDefault is needed often: forms reload the page by default, and links navigate away.
Common event props
- Mouse: onClick, onDoubleClick, onMouseEnter, onMouseLeave
- Keyboard: onKeyDown, onKeyUp
- Form: onChange, onSubmit, onInput
- Focus: onFocus, onBlur
- Touch: onTouchStart, onTouchEnd
One difference is worth knowing: React's onChange on a text input fires on every keystroke, like the native input event, rather than only when the field loses focus.
Updating state from an event
function Toggle() {
const [on, setOn] = useState(false);
return (
<button onClick={() => setOn(!on)}>
{on ? "On" : "Off"}
</button>
);
}Bubbling and stopping it
Events travel up through parent components, which is usually what you want. When a nested control should not trigger its container, stop propagation.
function Card({ onOpen }) {
return (
<div className="card" onClick={onOpen}>
<h3>Report</h3>
<button
onClick={event => {
event.stopPropagation();
console.log("Menu only, card not opened");
}}
>
More
</button>
</div>
);
}Handlers as props
Components you write receive functions as ordinary props. The convention is to name the prop onSomething and the function that implements it handleSomething.
function Toolbar({ onAdd, onRemove }) {
return (
<div>
<button onClick={onAdd}>Add item</button>
<button onClick={onRemove}>Remove item</button>
</div>
);
}
function App() {
function handleAdd() { /* ... */ }
function handleRemove() { /* ... */ }
return <Toolbar onAdd={handleAdd} onRemove={handleRemove} />;
}