React Event Handling
Handle user interactions using synthetic events, inline handlers, parameter passing, and default prevention.
TL;DR
- Attach camelCase event handlers like
onClickdirectly to JSX tags. - Pass handler function references instead of invoking them inside
JSXmarkup. - Prevent native browser reloads using the
preventDefaultevent method.
Basic Event Handlers
Named Function HandlerReference a declared handler function cleanly in JSX.
export function Button() {
function handleClick() {
console.log('Button clicked!');
}
return (
<ButtonBase onClick={handleClick}>
Go
</ButtonBase>
);
}Inline Arrow HandlerWrite concise one-line handlers directly in JSX attributes.
<ButtonBase onClick={() => console.log('Clicked')}>
Quick Action
</ButtonBase>Passing ArgumentsPass dynamic parameters using an arrow function wrapper.
<ButtonBase onClick={() => removeUser(user.id)}>
Delete User
</ButtonBase>Form and Input Events
Form Submit PreventionPrevent page reload and process form payload in React.
function handleSubmit(e: React.FormEvent) {
e.preventDefault();
// process submission
}
return <Form onSubmit={handleSubmit}>...</Form>;Input Change EventRead input value from target inside change handler.
function handleChange(
e: React.ChangeEvent<HTMLInputElement>
) {
console.log(e.target.value);
}
return <InputBase onChange={handleChange} />;Keyboard Key DownRespond to specific key presses like Enter or Escape.
function handleKeyDown(e: React.KeyboardEvent) {
if (e.key === 'Enter') submitSearch();
}
return <InputBase onKeyDown={handleKeyDown} />;Event Propagation
Stop PropagationPrevent event from bubbling up to parent containers.
function handleChildClick(e: React.MouseEvent) {
e.stopPropagation();
doChildAction();
}Capture Phase EventsIntercept events during the capture phase using Capture suffix.
<Card onClickCapture={() => console.log('Capture')}>
<ButtonBase>Click</ButtonBase>
</Card>Synthetic Event PoolingIn modern React, event objects persist across async calls.
async function handleAsyncClick(e: React.MouseEvent) {
const tag = e.currentTarget.tagName;
await fetch('/api/log');
console.log('Clicked:', tag);
}Pointer and Focus Events
Hover State TriggersTrack mouse entry and exit with pointer events.
<CardView
onMouseEnter={() => setHover(true)}
onMouseLeave={() => setHover(false)}
/>Focus and BlurHandle element focus and loss of focus cleanly.
<InputBase
onFocus={() => setShowHints(true)}
onBlur={() => setShowHints(false)}
/>Wheel Scroll HandlerCapture user mouse wheel movement events.
<CardView onWheel={(e) => console.log(e.deltaY)} />Tips
- Pass a function reference like
onClick={handleClick}rather than invoking it immediately with parenthesis inside the JSX template. - Use TypeScript
React.MouseEventorReact.ChangeEventtypes to gain complete autocompletion and safety on event target elements.
Warnings
- Writing
onClick={handleClick()}invokes your handler on initial render, causing unwanted infinite re-render loops in stateful components. - Remember that React synthetic events bubble up the component hierarchy unless explicitly intercepted with the
stopPropagationmethod.
In Practice
Implement a controlled search form handling submits, keyboard shortcuts, and resets.
- Intercept default form submission with preventDefault.
- Capture Escape key presses to clear the search query.
- Extract the current search string from event targets.
- Prevent parent propagation on clear button clicks.
export function SearchForm({
onSearch,
}: {
onSearch: (q: string) => void;
}) {
function handleSubmit(e: React.FormEvent) {
e.preventDefault();
const form = e.currentTarget as HTMLFormElement;
const data = new FormData(form);
onSearch(data.get('query') as string);
}
return (
<Form onSubmit={handleSubmit}>
<InputBase name="query" placeholder="Search" />
<ButtonBase type="submit">Go</ButtonBase>
</Form>
);
}FAQ
SyntheticEvent is a cross-browser wrapper around native browser events. It normalizes differences between browser implementations so your code behaves identically everywhere.
Wrap the function call in an inline arrow function, such as onClick={() => handleDelete(item.id)}, so execution is deferred until the user clicks.
Standard HTML forms submit via HTTP GET or POST, reloading the browser. Call e.preventDefault() in your onSubmit handler to intercept the submission in JavaScript.