Event handling looks simple until an interviewer probes the synthetic event system, argument passing, or why a handler fired at the wrong time. The questions below cover how React normalizes events, the reference-versus-call trap that catches beginners, preventDefault and stopPropagation, React's delegation model, and the small performance points around creating handlers. Ground your answers in "React wraps native events and delegates them from the root" and the rest follows.
How to answer event-handling questions
Two ideas cover most questions: React passes your JSX handler a SyntheticEvent — a cross-browser wrapper around the native event — and you give the prop a function reference, not a call. From there, argument passing, preventDefault, and delegation are natural extensions. When a "why did this fire wrong" question comes up, it is almost always a reference-versus-call or a bubbling issue; name which and you have the answer.
Q1. What is a SyntheticEvent?
A SyntheticEvent is React's cross-browser wrapper around the native DOM event. It exposes the same interface (target, type, preventDefault, stopPropagation) but normalizes differences between browsers, so a handler behaves identically everywhere. The underlying native event is available via event.nativeEvent when you need it.
The value is consistency: you write one handler and React smooths over browser quirks. It also plugs into React's own dispatch system rather than relying on raw DOM listeners scattered across elements.
Interview note: Follow-up: "is event pooling still a thing?" React used to pool and reuse synthetic event objects (requiring
e.persist()for async access), but that pooling was removed in React 17. In current React you can read the event asynchronously withoutpersist().
Q2. Why do you pass a reference, not a call, to an event prop?
onClick={handleClick} passes the function so React calls it when the event fires. onClick={handleClick()} calls it during render and passes the return value — usually undefined — so it fires immediately and not on click. To pass arguments, wrap it: onClick={() => handleClick(id)}.
<button onClick={handleClick}>Fires on click</button>
<button onClick={handleClick()}>Runs on render — bug</button>
<button onClick={() => handleClick(id)}>Fires on click, with arg</button>
Interview note: Trap: "my handler runs on every render and I never clicked — why?" You wrote
onClick={handleClick()}, invoking it during render. Remove the parentheses or wrap it in an arrow.
Q3. How do you pass arguments to an event handler?
Wrap the handler in an inline arrow function that calls it with the argument — onChange={(e) => handleField(name, e.target.value)} — or curry the handler so it returns a function. The arrow form is the clearest and most common; both preserve access to the event if you forward it.
{items.map(item => (
<li key={item.id} onClick={() => onSelect(item.id)}>{item.name}</li>
))}
Interview note: Follow-up: "does the inline arrow hurt performance?" It creates a new function each render, which only matters if the child is wrapped in
React.memoand relies on a stable prop — then useuseCallbackor restructure. For plain DOM elements it is negligible.
Q4. What do preventDefault and stopPropagation do?
preventDefault() cancels the browser's default action for the event — stopping a form from reloading the page, a link from navigating, a checkbox from toggling. stopPropagation() stops the event from bubbling up to ancestor handlers. They are independent: you often call one without the other.
function onSubmit(e) {
e.preventDefault(); // stop the native form reload
save();
}
function onInnerClick(e) {
e.stopPropagation(); // don't trigger the parent's onClick
}
Interview note: Trap: "you called
stopPropagationto stop a form reload — did it work?" No — bubbling and default actions are different. Stopping propagation does not prevent the default submit; you needpreventDefault.
Q5. How does React attach event listeners under the hood?
React uses event delegation. Rather than attaching a listener to every element, it attaches one listener per event type at the root container (the app root in React 17+, previously document) and routes each event to the correct component handler by walking its internal tree. This reduces memory and setup cost, especially for large or dynamic lists.
The practical implication: your onClick on a list item is not a real DOM listener on that <li>; React captured the event at the root and dispatched it. This is why mixing React handlers with manually attached native listeners can behave unexpectedly.
Interview note: Follow-up: "where does React attach the root listener now?" Since React 17 it attaches to the root DOM container you render into, not
document— which lets multiple React versions or apps coexist on one page without event conflicts.
Q6. How do the SyntheticEvent and native event interact with bubbling?
React's synthetic events follow the same capture-and-bubble model as native events, but they are dispatched by React's system from the root. If you mix a native listener (via a ref and addEventListener) with React handlers, ordering can surprise you because React's dispatch happens at the root, potentially after a native listener on a child. Knowing this boundary is a strong signal.
Interview note: Trap: "your native
addEventListenerhandler ran before your ReactonClick— why?" The native listener fires during the real DOM bubble on that element; React's synthetic handler fires when the event reaches the root. Prefer React handlers unless you specifically need the native one.
Q7. How do you handle keyboard events and accessibility?
Use onKeyDown/onKeyUp and check event.key (e.g. "Enter", "Escape", "ArrowDown") rather than deprecated key codes. For accessibility, ensure interactive custom elements are focusable and respond to keyboard as well as click — a <div> acting as a button needs tabIndex, a role, and key handling, whereas a real <button> gives you all of that for free.
function onKeyDown(e) {
if (e.key === "Enter" || e.key === " ") { e.preventDefault(); activate(); }
}
Interview note: Follow-up: "why prefer a real
<button>over a clickable<div>?" The button is focusable, keyboard-activatable and announced correctly by screen readers by default; the div needsrole,tabIndexand manual key handling to match.
Q8. What are common event-handling mistakes interviewers watch for?
Calling the handler instead of passing it (onClick={fn()}), forgetting preventDefault on form submit, confusing stopPropagation with preventDefault, creating handlers that capture stale state (a stale closure), and adding native listeners without cleanup. Each is a small bug that reveals whether you understand the model or copied a pattern.
Interview note: Trap: "your click handler logs an old value of state — why?" Stale closure: the handler captured state from an earlier render. Use the functional updater, keep dependencies honest, or store the latest value in a ref.
Q9. How do you optimize event handlers in performance-sensitive components?
Most handlers need no optimization. When a handler is passed to a memoized child, wrap it in useCallback so the child does not re-render on a new reference; when a handler must not fire too often (scroll, resize, live search), debounce or throttle it. Otherwise, inline arrow handlers are fine. The performance optimization set covers when reference stability actually matters.
const onSelect = useCallback((id) => dispatch(select(id)), [dispatch]);
// Stable reference — a React.memo child won't re-render because of this prop
Interview note: Follow-up: "you wrapped the handler in
useCallbackbut the child still re-renders — why?" The child is not memoized, or another prop changes each render.useCallbackonly helps aReact.memochild whose other props are also stable.
How to prepare
Wire up a small interactive component — a list where each row passes its id to a handler, a form with onSubmit and preventDefault, and a keyboard-accessible custom control — and deliberately trigger the classic bugs: the immediate-fire from fn(), the stale-closure log, the stopPropagation-instead-of-preventDefault mix-up. Fixing each once makes the concept stick. Rehearse the SyntheticEvent and delegation explanations, since those are the "do you understand the model" questions. Pair this with the forms handling set (submission and change events overlap heavily) and the components and props foundation, refresh details in the React learning path, and use a mock interview to practise the reference-versus-call and bubbling follow-ups out loud.
Frequently Asked Questions
What is a synthetic event in React?
How do you pass an argument to an event handler?
Why is my onClick firing immediately on render?
How does React attach event listeners?
How do preventDefault and stopPropagation differ?
Want to Build Your Career in Java Full Stack with AI?
Join CodeBegun and train with working industry engineers — Explore the Java Full Stack program

