Event handlers in React
React lets you add event handlers to JSX. Event handlers are functions that are triggered in response to user interactions like clicking, hovering, and focusing on form inputs. Built-in components like button only support built-in browser events like onClick. Custom components can have event handler props with any application-specific names.
Event handler example with custom component props
Custom components can accept event handler props with application-specific names. Example: A Toolbar component accepts onPlayMovie and onUploadImage props, then passes them to Button components which map them to the onClick prop of built-in button elements.
Event handler functions and onClick
You can respond to events by declaring event handler functions inside your components. Pass the function to an event attribute like `onClick={handleClick}` without parentheses. Do not call the function; React will call it when the user triggers the event.
Where to cause side effects: event handlers
Side effects should usually belong inside event handlers. Event handlers are functions that React runs when you perform some action (for example, when you click a button). Even though event handlers are defined inside a component, they do not run during rendering. Therefore, event handlers do not need to be pure.
Five-step process to think about UI declaratively
When developing a component with state: (1) Identify all its visual states. (2) Determine the human and computer triggers for state changes. (3) Model the state with useState. (4) Remove non-essential state to avoid bugs and paradoxes. (5) Connect the event handlers to set state.
stopPropagation prevents event bubbling
Call e.stopPropagation() in an event handler to prevent the event from bubbling up to parent elements. This is useful when you want a child element's click handler to run without triggering parent click handlers.
Difference between stopPropagation and preventDefault
These two methods do different things. `e.stopPropagation()` stops the event from reaching parent element handlers—it stops the event from bubbling up. `e.preventDefault()` stops the browser's default behavior for that event (like a form reload or link navigation). They are independent and often both are needed, or neither, depending on the desired behavior.
Event handlers are ideal for side effects
Event handlers are the best place for side effects in React components. Unlike rendering functions, event handlers do not need to be pure functions and can change things. You can modify input values in response to typing or change lists in response to button clicks. To change information, you need state to store it.
Use appropriate HTML elements for event handlers
Use correct HTML elements for event handlers. For clicks, use `<button onClick={handleClick}>` instead of `<div onClick={handleClick}>`. Real browser buttons provide built-in behaviors like keyboard navigation for accessibility. You can style buttons to look like other UI elements using CSS if desired, but start with semantic HTML.
Alternative to propagation: explicit handler chaining
Instead of relying on event propagation, you can explicitly call the parent's event handler from the child handler before or after handling the event locally. This pattern, like `onClick={e => { e.stopPropagation(); onClick(); }}`, provides an alternative to propagation. The benefit is that you can clearly see the entire chain of code execution as a result of an event, making it easier to trace and debug.
Events propagate up the component tree
Events bubble or propagate upward from the element where they occurred. When you click a button inside a div, the button's onClick handler fires first, then the div's onClick handler fires. This continues up the tree through all parent elements. All events propagate in React except `onScroll`, which only works on the element it is attached to.
Stop event propagation with e.stopPropagation()
Event handlers receive an event object (conventionally called `e`) as their only argument. You can call `e.stopPropagation()` on this object to prevent the event from bubbling up to parent elements. This stops any parent element's event handlers from firing. For example, `onClick={e => { e.stopPropagation(); onClick(); }}` prevents the event from reaching parent handlers while still executing the current handler's logic.
Prevent default browser behavior with e.preventDefault()
Call `e.preventDefault()` on the event object to stop default browser behaviors. For example, form submit events reload the page by default; calling `e.preventDefault()` stops this. Do not confuse this with `e.stopPropagation()`: `preventDefault()` prevents the browser's default action while `stopPropagation()` prevents event bubbling to parent handlers.
Capture phase events with onClickCapture
Add `Capture` at the end of an event name to handle events in the capture phase, which occurs before the bubbling phase. For example, `onClickCapture={() => {}}` runs during the capture phase. Events propagate in three phases: capture (traveling down), element handler (running on the clicked element), and bubbling (traveling up). Capture events run even if a child element called `stopPropagation()`. This is useful for analytics or routers but uncommon in app code.
Event handlers must be passed, not called
Functions passed to event handlers must be passed as a reference, not called immediately. For example, use `onClick={handleClick}` instead of `onClick={handleClick()}`. Using parentheses fires the function immediately during rendering without any user interaction, because JavaScript inside JSX curly braces executes right away. The difference is subtle but critical: `handleClick` passes the function to React to call later on user interaction, while `handleClick()` executes the code during render.
Inline event handlers must be wrapped in functions
When using inline code for event handlers, wrap it in an anonymous function. For example, use `onClick={() => alert('clicked')}` instead of `onClick={alert('clicked')}`. Without the wrapper function, the code fires every time the component renders, not when clicked. This pattern applies to both named functions and inline code.
Event handler naming convention
Event handler functions are usually named with `handle` followed by the event name, such as `handleClick`, `handleMouseEnter`, or `handleSubmit`. Event handler props in custom components should start with `on` followed by a capital letter, such as `onClick`, `onMouseEnter`, or custom names like `onSmash`. This convention makes event handling code consistent and predictable.
Event handlers are declared inside components
Event handlers are typically defined inside your component function, which gives them access to the component's props and state. This allows handlers to use props when responding to events, such as showing an alert with a message prop when clicked.
Passing event handlers as props from parent to child
Parent components can specify child event handlers by passing a function as a prop. The child component receives the handler as a prop and passes it to an element like `<button onClick={onClick}>`. This pattern allows different parent components to use the same child component with different behaviors, such as a reusable Button component used for playing movies or uploading images depending on the parent.
Custom event handler prop names should reflect app concepts
When building custom components that support multiple interactions, name event handler props based on app-specific concepts rather than just `onClick`. For example, use `onPlayMovie` and `onUploadImage` instead of generic names. This provides flexibility to change implementation details later without affecting the component's external interface.
useTransition hook for server action transitions
In client components, use the useTransition hook to track when a server action is pending and to start the transition. The hook returns [pending, startTransition] where pending is a boolean indicating if the action is in progress. Example: const [pending, startTransition] = useTransition(); startTransition(async () => { await serverAction(); });
Form action attribute calls server action
Server actions can be called directly via the form action attribute without JavaScript event handlers. Example: <form action={addLike}><button type="submit">Like</button></form> This works because the server action is serialized and sent to the client via React Flight.
Event handlers are not reactive
Logic inside event handlers is not reactive. Event handlers only run when you perform the same interaction again, such as clicking a button. Event handlers can read reactive values like props and state without reacting to their changes. For example, if an event handler reads a message state variable and sends it, changing the message does not trigger the event handler to run again.
Controlled form input pattern
A controlled input has its value set by a prop and requires an onChange handler to update state. Writing <input value={filterText} /> without an onChange handler creates a read-only field and React will ignore user input. To make the input interactive, pass the state value as the value prop and add an onChange handler that calls a callback function to update the parent's state with the new input value.
React.SyntheticEvent for uncommon DOM events
If you need to use a DOM event not included in React's event type list, use the React.SyntheticEvent type, which is the base type for all React events.
Type React component props with interface or inline types
React component props can be typed using inline syntax directly in the function parameter, or by defining an interface or type to describe the props object. Using an interface or type is preferred when props become numerous or complex.
Example: typing React component props with interface
interface MyButtonProps {
/** The text to display inside the button */
title: string;
/** Whether the button can be interacted with */
disabled: boolean;
}
function MyButton({ title, disabled }: MyButtonProps) {
return (
<button disabled={disabled}>{title}</button>
);
}
Typing DOM event handlers in React TypeScript
When extracting a function to be passed to an event handler, explicitly set the type of the event parameter using React event types like React.ChangeEvent<HTMLInputElement>. The event type can often be inferred when the handler is inline.
Example: typing onChange event handler
import { useState } from 'react';
export default function Form() {
const [value, setValue] = useState("Change me");
function handleChange(event: React.ChangeEvent<HTMLInputElement>) {
setValue(event.currentTarget.value);
}
return (
<>
<input value={value} onChange={handleChange} />
<p>Value: {value}</p>
</>
);
}
React event handlers use camelCase naming
React event handler props use camelCase names like onClick, onChange, onSubmit. This differs from HTML's lowercase onclick. By convention, handler function names use handleSomething and event prop names use onSomething.
tic-tac-toe example - Square component structure
function Square({ value, onSquareClick }) {
return (
<button className="square" onClick={onSquareClick}>
{value}
</button>
);
}
The Square component accepts a value prop to display and an onSquareClick prop that is a callback function triggered when the button is clicked.
Arrow functions defer function execution in event handlers
Arrow functions with the syntax () => functionName(args) allow you to pass arguments to event handlers without executing the function immediately during render. The function runs only when the event occurs.
onClick prop receives a function, not a function call
Event handlers like onClick should receive a function reference, not the result of calling a function. For example, onClick={handleClick} passes the function itself. Calling handleClick immediately as onClick={handleClick()} executes the function during render and causes infinite loops. Use arrow functions to pass arguments: onClick={() => handleClick(0)}.
Calculate state values in event handlers
Instead of chaining Effects, calculate all next state values in the event handler before updating state. This allows all updates to happen in a single pass.
POST request on form submit should be in event handler
A POST request that sends form data on submit should be in the form's submit event handler, not in an Effect. The request should only happen when the user clicks submit, not when the component displays.
Event-specific logic should be in event handlers
Code that should run because of a specific user interaction should be in event handlers, not Effects. Effects run because a component was displayed, not because of a specific user action. Using an Effect for event-specific logic can cause bugs like unexpected behavior on page reload.
Share logic between event handlers
When multiple event handlers need the same logic, extract it into a shared function and call it from both handlers. This is simpler and less error-prone than putting the logic in an Effect, which could run at unexpected times.