[{"data":1,"prerenderedAt":123},["ShallowReactive",2],{"qa-\u002Freact\u002Fcomponents\u002Fevent-handling":3},{"page":4,"siblings":103,"blog":120},{"id":5,"title":6,"body":7,"description":11,"difficulty":14,"extension":15,"framework":16,"frameworkSlug":17,"meta":18,"navigation":20,"order":21,"path":22,"questions":23,"questionsCount":94,"related":95,"seo":96,"seoDescription":97,"stem":98,"subtopic":6,"topic":99,"topicSlug":100,"updated":101,"__hash__":102},"qa\u002Freact\u002Fcomponents\u002Fevent-handling.md","Event Handling",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"easy","md","React","react",{"subtopicSlug":19},"event-handling",true,3,"\u002Freact\u002Fcomponents\u002Fevent-handling",[24,28,33,37,41,45,49,53,57,61,65,69,73,77,82,86,90],{"id":25,"difficulty":14,"q":26,"a":27},"attach-event-handler","How do you attach an event handler to an element in React?","Pass a **function reference** as a camelCase prop on the JSX element.\nReact registers the handler for you via its internal event system.\n\n```jsx\nfunction ClickCounter() {\n  const [count, setCount] = useState(0)\n\n  function handleClick() {\n    setCount(c => c + 1)\n  }\n\n  return \u003Cbutton onClick={handleClick}>Clicked {count} times\u003C\u002Fbutton>\n}\n```\n\nCommon pitfalls:\n- `onClick={handleClick()}` — calls the function immediately during render\n  and passes its return value. Correct form omits the `()`.\n- Arrow functions inline are fine but create a new function each render,\n  which can matter for memoized children.\n\n**Rule of thumb:** name handlers `handleEventName` by convention; pass\nthem as `onEventName={handler}` — no parentheses.\n",{"id":29,"difficulty":30,"q":31,"a":32},"synthetic-event","medium","What is a SyntheticEvent in React?","React wraps native browser events in a **SyntheticEvent** — a cross-browser\nwrapper that normalizes differences in the native event API (e.g. IE vs\nChrome). It exposes the same interface as the native event\n(`target`, `preventDefault()`, `stopPropagation()`, etc.) but works\nconsistently everywhere.\n\n```jsx\nfunction Input() {\n  function handleChange(e) {\n    \u002F\u002F e is a SyntheticEvent\n    console.log(e.target.value)   \u002F\u002F same API as native InputEvent\n    console.log(e.nativeEvent)    \u002F\u002F access the raw browser event\n  }\n  return \u003Cinput onChange={handleChange} \u002F>\n}\n```\n\n**React 17 change:** prior to React 17, synthetic events were **pooled**\n(reused after the handler returned, with all properties set to `null`). In\nReact 17+, pooling was removed — you can safely access the event\nasynchronously.\n\n**Rule of thumb:** treat a SyntheticEvent exactly like a native DOM event.\nIf you need the raw event, it's on `e.nativeEvent`.\n",{"id":34,"difficulty":14,"q":35,"a":36},"camelcase-events","Why do React event names use camelCase?","Because JSX is JavaScript, not HTML. In HTML, event attributes are lowercase\nstrings (`onclick`, `onmouseover`). In JSX, they are **JavaScript property\nnames** on the props object, and the React team chose camelCase as the\nconvention to match JavaScript idioms.\n\n```jsx\n\u002F\u002F HTML\n\u002F\u002F \u003Cbutton onclick=\"handler()\">\n\n\u002F\u002F JSX\n\u003Cbutton onClick={handler}>\n\n\u002F\u002F More examples\n\u003Cinput onChange={handle} onFocus={handle} onBlur={handle} \u002F>\n\u003Cform onSubmit={handle} \u002F>\n\u003Cdiv onMouseEnter={handle} onMouseLeave={handle} \u002F>\n```\n\n**Rule of thumb:** every DOM event is available in React as `on` +\nPascalCase (e.g. `click` → `onClick`, `keydown` → `onKeyDown`).\n",{"id":38,"difficulty":14,"q":39,"a":40},"prevent-default","How do you prevent default browser behavior in React?","Call `e.preventDefault()` on the SyntheticEvent inside your handler.\n\n```jsx\nfunction LoginForm() {\n  function handleSubmit(e) {\n    e.preventDefault()          \u002F\u002F stops the full-page form POST\n    \u002F\u002F ... handle submission with fetch\n  }\n  return (\n    \u003Cform onSubmit={handleSubmit}>\n      \u003Cinput name=\"email\" type=\"email\" \u002F>\n      \u003Cbutton type=\"submit\">Log in\u003C\u002Fbutton>\n    \u003C\u002Fform>\n  )\n}\n```\n\nCommon use cases:\n- `\u003Cform>` `onSubmit` — prevent page reload.\n- `\u003Ca>` `onClick` — prevent navigation to `href`.\n- Drag events — prevent the default drop behavior.\n\n**Rule of thumb:** call `preventDefault()` at the top of your handler\nbefore any async work — after an `await`, the event may have already\nbeen handled by the browser.\n",{"id":42,"difficulty":30,"q":43,"a":44},"return-false","Why can't you return false to prevent default behavior in React?","In plain HTML event attributes (`onclick=\"return false\"`), returning `false`\nwas a shortcut that both prevented the default and stopped propagation.\nReact **does not** support this shortcut.\n\nReact event handlers are regular JavaScript functions — returning `false`\nfrom them has no special meaning to React's event system.\n\n```jsx\n\u002F\u002F ❌ Doesn't work in React — return value is ignored\n\u003Ca href=\"\u002Fhome\" onClick={() => false}>Go\u003C\u002Fa>\n\n\u002F\u002F ✅ Explicit calls are required\n\u003Ca\n  href=\"\u002Fhome\"\n  onClick={e => {\n    e.preventDefault()\n    e.stopPropagation()\n  }}\n>\n  Go\n\u003C\u002Fa>\n```\n\n**Rule of thumb:** always call `e.preventDefault()` and\u002For\n`e.stopPropagation()` explicitly; never rely on return values.\n",{"id":46,"difficulty":30,"q":47,"a":48},"stop-propagation","What is the difference between stopPropagation and preventDefault?","They control two completely different things:\n\n- **`e.preventDefault()`** — stops the browser from running its built-in\n  action for the event (e.g. form submission, link navigation, checkbox\n  toggle). The event still bubbles up through the DOM.\n\n- **`e.stopPropagation()`** — stops the event from bubbling further up the\n  DOM tree. Parent handlers won't fire. Does not affect the browser action.\n\n```jsx\nfunction List() {\n  return (\n    \u003Cul onClick={() => console.log('list clicked')}>\n      \u003Cli>\n        \u003Ca\n          href=\"\u002Fdetail\"\n          onClick={e => {\n            e.preventDefault()    \u002F\u002F don't navigate to \u002Fdetail\n            e.stopPropagation()   \u002F\u002F don't trigger the ul's onClick\n            navigate('\u002Fmodal')\n          }}\n        >\n          View\n        \u003C\u002Fa>\n      \u003C\u002Fli>\n    \u003C\u002Ful>\n  )\n}\n```\n\n**Rule of thumb:** call `preventDefault` to suppress browser behavior;\ncall `stopPropagation` to prevent parent handlers from also running.\nYou often need both for nested interactive elements.\n",{"id":50,"difficulty":30,"q":51,"a":52},"passing-arguments","How do you pass arguments to an event handler?","Wrap the handler in an arrow function or use `.bind`. The arrow-function\napproach is more common in JSX.\n\n```jsx\nfunction ItemList({ items }) {\n  function handleDelete(id, e) {\n    e.stopPropagation()\n    deleteItem(id)\n  }\n\n  return (\n    \u003Cul>\n      {items.map(item => (\n        \u003Cli key={item.id}>\n          {item.name}\n          {\u002F* Arrow wrapper — creates a new fn per render *\u002F}\n          \u003Cbutton onClick={e => handleDelete(item.id, e)}>Delete\u003C\u002Fbutton>\n        \u003C\u002Fli>\n      ))}\n    \u003C\u002Ful>\n  )\n}\n```\n\nFor performance-sensitive lists, you can use data attributes instead:\n\n```jsx\nfunction handleDelete(e) {\n  const id = e.currentTarget.dataset.id\n  deleteItem(id)\n}\n\n\u003Cbutton data-id={item.id} onClick={handleDelete}>Delete\u003C\u002Fbutton>\n```\n\n**Rule of thumb:** arrow wrappers are fine for most use cases. Use\n`data-*` attributes on large lists where creating many closures is\nmeasurably expensive.\n",{"id":54,"difficulty":30,"q":55,"a":56},"event-delegation","How does event delegation work in React?","React does not attach individual event listeners directly to each DOM node.\nInstead, it attaches **a single listener** at the root and uses event\nbubbling to catch all events — this is called event delegation.\n\nPrior to React 17, all events were delegated to `document`. In React 17+,\nthey are delegated to the **root DOM container** (`document.getElementById('root')`).\nThis change makes it safer to embed multiple React apps on one page or mix\nReact with non-React code.\n\n```jsx\n\u002F\u002F Conceptually what React does internally (simplified)\nrootElement.addEventListener('click', react_internal_handler)\n\u002F\u002F When a \u003Cbutton> inside is clicked, the event bubbles up to root,\n\u002F\u002F React checks which component was the target, and calls your onClick.\n```\n\nThis is largely transparent to application code, but matters when:\n- Calling `e.stopPropagation()` — stops bubbling before it reaches the\n  React root, so React never sees the event.\n- Using `addEventListener` directly on `document` — fires after React's\n  handler in React 17+.\n\n**Rule of thumb:** rely on React's event system rather than attaching your\nown listeners to `document`; it handles delegation and cleanup for you.\n",{"id":58,"difficulty":14,"q":59,"a":60},"form-handling","How do you handle a form submission in React?","Attach `onSubmit` to the `\u003Cform>` element, call `e.preventDefault()` to\nstop the page reload, then read values from controlled state or uncontrolled\nrefs.\n\n```jsx\nfunction SignupForm() {\n  const [email, setEmail] = useState('')\n  const [password, setPassword] = useState('')\n\n  async function handleSubmit(e) {\n    e.preventDefault()\n    await signUp({ email, password })\n  }\n\n  return (\n    \u003Cform onSubmit={handleSubmit}>\n      \u003Cinput\n        type=\"email\"\n        value={email}\n        onChange={e => setEmail(e.target.value)}\n      \u002F>\n      \u003Cinput\n        type=\"password\"\n        value={password}\n        onChange={e => setPassword(e.target.value)}\n      \u002F>\n      \u003Cbutton type=\"submit\">Sign up\u003C\u002Fbutton>\n    \u003C\u002Fform>\n  )\n}\n```\n\n**Rule of thumb:** always put `onSubmit` on `\u003Cform>`, not `onClick` on\nthe submit button — the form's handler fires for Enter key presses as well\nas button clicks.\n",{"id":62,"difficulty":30,"q":63,"a":64},"controlled-input","What is a controlled input and why does React prefer it?","A **controlled input** has its `value` (or `checked`) driven by React\nstate. React becomes the single source of truth for the input's content.\n\n```jsx\n\u002F\u002F Controlled — React owns the value\nconst [text, setText] = useState('')\n\u003Cinput value={text} onChange={e => setText(e.target.value)} \u002F>\n\n\u002F\u002F Uncontrolled — DOM owns the value, React reads it on demand\nconst ref = useRef()\n\u003Cinput ref={ref} \u002F>\n\u002F\u002F later: ref.current.value\n```\n\nWhy React prefers controlled:\n- You can validate or transform input on every keystroke.\n- Input state is immediately available in component scope — no need to\n  query the DOM.\n- The input reflects state exactly, so programmatic resets work:\n  `setText('')` clears the field.\n\n**Rule of thumb:** use controlled inputs by default; use uncontrolled when\nintegrating with non-React DOM libraries or when you only need the value\nat submission.\n",{"id":66,"difficulty":30,"q":67,"a":68},"onchange-vs-oninput","What is the difference between onChange and onInput in React?","In the browser, `input` fires on every value change (typing, pasting,\ncutting), while `change` fires only when the element loses focus after a\nvalue change.\n\nReact **normalizes** this: React's `onChange` fires on every value change\n(like the native `input` event), making it consistent regardless of element\ntype. React's `onChange` and native `oninput` are effectively equivalent for\ntext inputs.\n\n```jsx\n\u002F\u002F Fires on every keystroke in React (not just on blur like native 'change')\n\u003Cinput onChange={e => console.log(e.target.value)} \u002F>\n```\n\nReact does expose `onInput` as well, but for controlled inputs `onChange`\nis the standard and recommended handler.\n\n**Rule of thumb:** use `onChange` in React for real-time input tracking.\nKnowing that it behaves like native `oninput` matters when comparing React\nbehavior with vanilla JS.\n",{"id":70,"difficulty":30,"q":71,"a":72},"keyboard-events","How do you handle keyboard events in React?","Use `onKeyDown`, `onKeyUp`, or `onKeyPress` (deprecated — avoid). Read\n`e.key` for the logical key name or `e.code` for the physical key.\n\n```jsx\nfunction SearchBox() {\n  const [query, setQuery] = useState('')\n\n  function handleKeyDown(e) {\n    if (e.key === 'Enter') {\n      search(query)\n    }\n    if (e.key === 'Escape') {\n      setQuery('')\n    }\n  }\n\n  return (\n    \u003Cinput\n      value={query}\n      onChange={e => setQuery(e.target.value)}\n      onKeyDown={handleKeyDown}\n    \u002F>\n  )\n}\n```\n\nFor accessibility, elements that handle keyboard events should also be\nfocusable (native interactive elements are by default; custom elements need\n`tabIndex={0}` and appropriate ARIA roles).\n\n**Rule of thumb:** check `e.key` (logical, locale-aware) rather than\n`e.keyCode` (deprecated numeric code). For modifier keys, check\n`e.ctrlKey`, `e.shiftKey`, `e.altKey`, `e.metaKey`.\n",{"id":74,"difficulty":30,"q":75,"a":76},"class-this-binding","How does 'this' binding work in class component event handlers?","In a class component, `this` inside a regular method is `undefined` when\ncalled as an event handler (because it's passed as a callback — detached\nfrom the instance). There are three common fixes:\n\n```jsx\nclass Counter extends React.Component {\n  constructor(props) {\n    super(props)\n    this.state = { n: 0 }\n    \u002F\u002F Option 1: bind in constructor\n    this.handleClick = this.handleClick.bind(this)\n  }\n\n  handleClick() {\n    this.setState({ n: this.state.n + 1 })\n  }\n\n  render() {\n    return \u003Cbutton onClick={this.handleClick}>{this.state.n}\u003C\u002Fbutton>\n  }\n}\n\n\u002F\u002F Option 2: class field arrow function (auto-binds)\nclass Counter extends React.Component {\n  state = { n: 0 }\n  handleClick = () => {\n    this.setState({ n: this.state.n + 1 })\n  }\n  render() {\n    return \u003Cbutton onClick={this.handleClick}>{this.state.n}\u003C\u002Fbutton>\n  }\n}\n\n\u002F\u002F Option 3: arrow in render (new function per render)\n\u003Cbutton onClick={() => this.handleClick()}>\n```\n\n**Rule of thumb:** class fields (option 2) is the cleanest in modern JS.\nIn function components, this issue doesn't exist at all — another reason\nto prefer them.\n",{"id":78,"difficulty":79,"q":80,"a":81},"throttle-debounce","hard","How do you throttle or debounce an event handler in React?","Wrap the handler with a throttle\u002Fdebounce utility (lodash is most common)\nbut **memoize the wrapper** with `useRef` or `useCallback` so you get a\nstable function reference across renders.\n\n```jsx\nimport { useCallback, useRef } from 'react'\nimport debounce from 'lodash\u002Fdebounce'\n\nfunction SearchInput({ onSearch }) {\n  \u002F\u002F useRef keeps the debounced fn stable across renders\n  const debouncedSearch = useRef(\n    debounce((value) => onSearch(value), 300)\n  ).current\n\n  \u002F\u002F Clean up on unmount\n  useEffect(() => () => debouncedSearch.cancel(), [debouncedSearch])\n\n  return (\n    \u003Cinput onChange={e => debouncedSearch(e.target.value)} \u002F>\n  )\n}\n```\n\nCommon mistake: creating the debounced function inside the component body\nwithout memoization — a new function (with a fresh timer) is created each\nrender, so debouncing never actually fires.\n\n**Rule of thumb:** store debounced\u002Fthrottled functions in `useRef` (not\nrecreated on render) and cancel them on unmount to avoid calling stale\nclosures.\n",{"id":83,"difficulty":30,"q":84,"a":85},"native-event","How do you access the native DOM event from a React SyntheticEvent?","Use `e.nativeEvent` to get the underlying browser `Event` object.\n\n```jsx\nfunction DragZone() {\n  function handleDrop(e) {\n    e.preventDefault()\n    \u002F\u002F SyntheticEvent doesn't expose dataTransfer directly in all React versions\n    const files = e.nativeEvent.dataTransfer?.files\n    console.log(files)\n  }\n  return \u003Cdiv onDrop={handleDrop} onDragOver={e => e.preventDefault()}>Drop here\u003C\u002Fdiv>\n}\n```\n\nReasons you might need `nativeEvent`:\n- Accessing properties not forwarded by the SyntheticEvent wrapper.\n- Passing the event to a non-React library that checks `instanceof Event`.\n- Calling browser-only APIs like `getCoalescedEvents()` for pointer events.\n\n**Rule of thumb:** start with the SyntheticEvent API; only reach for\n`nativeEvent` when you need a property or method that the wrapper doesn't\nexpose.\n",{"id":87,"difficulty":30,"q":88,"a":89},"events-in-list","What is the most efficient way to handle events in a list of items?","Instead of passing a unique arrow function per item (which creates N\nclosures), use a single handler and identify the target via `data-*`\nattributes or `e.currentTarget`.\n\n```jsx\nfunction TodoList({ todos, onToggle }) {\n  function handleClick(e) {\n    const id = e.currentTarget.dataset.id\n    onToggle(id)\n  }\n\n  return (\n    \u003Cul>\n      {todos.map(todo => (\n        \u003Cli key={todo.id}>\n          \u003Cbutton data-id={todo.id} onClick={handleClick}>\n            {todo.text}\n          \u003C\u002Fbutton>\n        \u003C\u002Fli>\n      ))}\n    \u003C\u002Ful>\n  )\n}\n```\n\nFor most lists, arrow functions per item are fine — the overhead is\nnegligible. The `data-*` pattern is worth applying when the list is very\nlong or when the handlers are passed to `React.memo` children.\n\n**Rule of thumb:** profile before optimizing. Use `data-*` delegation when\nyou have hundreds of interactive rows and can measure the improvement.\n",{"id":91,"difficulty":79,"q":92,"a":93},"synthetic-event-async","What happened to SyntheticEvent pooling and what changed in React 17?","Before React 17, React **pooled** SyntheticEvents to reduce garbage\ncollection pressure. After your handler returned, React would reset all\nproperties to `null` and return the event object to the pool.\n\n```jsx\n\u002F\u002F Pre-React 17 bug\nfunction handleChange(e) {\n  setTimeout(() => {\n    console.log(e.target.value)  \u002F\u002F CRASH — e.target is null after handler exits\n  }, 100)\n}\n\n\u002F\u002F Pre-React 17 fix: persist the event\nfunction handleChange(e) {\n  e.persist()  \u002F\u002F remove from pool, keep properties\n  setTimeout(() => {\n    console.log(e.target.value)  \u002F\u002F safe\n  }, 100)\n}\n```\n\n**React 17 removed event pooling.** SyntheticEvents are now regular objects\nthat are not recycled. Accessing them asynchronously is safe, and\n`e.persist()` is a no-op (still exists to avoid breaking old code).\n\n**Rule of thumb:** in React 17+ projects you don't need `e.persist()`. If\nyou see it in a codebase, it's either legacy code or a defensive habit from\nolder React.\n",17,null,{"description":11},"React event handling interview questions — SyntheticEvent, camelCase events, preventDefault, stopPropagation, passing arguments, controlled inputs, and event delegation.","react\u002Fcomponents\u002Fevent-handling","Components","components","2026-06-23","ouA3PzTAevet2voyeMiClKokS89VxQIssKu_33PPQ1o",[104,108,111,112,116],{"subtopic":105,"path":106,"order":107},"JSX and Rendering","\u002Freact\u002Fcomponents\u002Fjsx-rendering",1,{"subtopic":109,"path":110,"order":12},"Props and Component Types","\u002Freact\u002Fcomponents\u002Fprops-component-types",{"subtopic":6,"path":22,"order":21},{"subtopic":113,"path":114,"order":115},"Conditional Rendering","\u002Freact\u002Fcomponents\u002Fconditional-rendering",4,{"subtopic":117,"path":118,"order":119},"Lists and Keys","\u002Freact\u002Fcomponents\u002Flists-keys",5,{"path":121,"title":122},"\u002Fblog\u002Freact-event-handling-guide","React Event Handling — A Complete Interview Guide",1782244100874]