Documentation Index
Fetch the complete documentation index at: https://resources.devweekends.com/llms.txt
Use this file to discover all available pages before exploring further.
Handling Events
Events are how users interact with your application. Every click, keystroke, form submission, and mouse movement fires an event that your components can respond to. React provides a consistent, cross-browser event system called Synthetic Events. Think of SyntheticEvents as a universal translator: no matter which browser your user is on (Chrome, Firefox, Safari), React normalizes the event into a single, consistent API so you never have to write browser-specific code.React Events vs DOM Events
| Feature | HTML/DOM | React |
|---|---|---|
| Naming | lowercase (onclick) | camelCase (onClick) |
| Handler | String ("handleClick()") | Function ({handleClick}) |
| Prevent default | return false; | e.preventDefault() |
| Event object | Browser-specific | Normalized (Synthetic) |
Basic Event Handling
Defining Event Handlers
Don’t Call the Function!
This is one of the most common beginner mistakes in React. You need to pass a reference to a function, not the result of calling it.The Event Object
React wraps native browser events in a SyntheticEvent for cross-browser compatibility.Common Event Properties
| Property | Description |
|---|---|
event.target | Element that triggered the event |
event.currentTarget | Element the handler is attached to |
event.type | Event type ('click', 'change', etc.) |
event.preventDefault() | Prevents default browser behavior |
event.stopPropagation() | Stops event bubbling |
event.nativeEvent | Underlying native browser event |
Passing Arguments to Event Handlers
Often you need to pass data (like an item ID) to the handler.Accessing Event AND Custom Arguments
Mouse Events
Mouse Event Types
| Event | Triggers When |
|---|---|
onClick | Mouse button clicked |
onDoubleClick | Mouse button double-clicked |
onMouseEnter | Cursor enters element (doesn’t bubble) |
onMouseLeave | Cursor leaves element (doesn’t bubble) |
onMouseOver | Cursor over element (bubbles) |
onMouseOut | Cursor leaves element (bubbles) |
onMouseMove | Cursor moves over element |
onMouseDown | Mouse button pressed |
onMouseUp | Mouse button released |
onContextMenu | Right-click (context menu) |
Keyboard Events
Key Event Properties
| Property | Description |
|---|---|
event.key | The key value ('Enter', 'a', 'ArrowUp') |
event.code | Physical key code ('KeyA', 'Enter') |
event.altKey | Alt key held |
event.ctrlKey | Ctrl key held |
event.shiftKey | Shift key held |
event.metaKey | Meta key held (Cmd on Mac) |
Building Keyboard Shortcuts
Form Events
Input Change Event
Focus Events
Event Propagation
Events bubble up through the DOM tree. You can stop this behavior.Event Capturing
React also supports the capture phase (rarely needed):Touch Events (Mobile)
Drag and Drop Events
Event Handler Pitfalls
🎯 Practice Exercises
Exercise 1: Like Button with Double-Click to Super Like
Exercise 1: Like Button with Double-Click to Super Like
Exercise 2: Keyboard-Controlled Character
Exercise 2: Keyboard-Controlled Character
Exercise 3: Click Outside to Close
Exercise 3: Click Outside to Close
Summary
| Concept | Description |
|---|---|
| Event Handlers | Functions that respond to user interactions |
| SyntheticEvent | React’s cross-browser event wrapper |
| event.target | Element that triggered the event |
| event.preventDefault() | Stop default browser behavior |
| event.stopPropagation() | Stop event bubbling |
| Mouse Events | click, doubleClick, mouseEnter, mouseLeave, etc. |
| Keyboard Events | keyDown, keyUp, keyPress (use key property) |
| Form Events | onChange, onSubmit, onFocus, onBlur |
| Touch Events | touchStart, touchMove, touchEnd (mobile) |
Next Steps
In the next chapter, you’ll learn about Lists & Keys — efficiently rendering collections of data!
Interview Deep-Dive
What are React SyntheticEvents, why do they exist, and how do they differ from native DOM events?
What are React SyntheticEvents, why do they exist, and how do they differ from native DOM events?
Strong Answer:
SyntheticEvents are React’s cross-browser wrapper around native DOM events. They exist because browser event implementations are inconsistent — different browsers name properties differently, bubble events differently, and handle default behaviors differently. React normalizes all of this into a single consistent API so you write event handling code once and it works everywhere.Under the hood, React does not attach event listeners to individual DOM nodes. Since React 17, it attaches a single event listener to the root DOM container (the element you pass to
createRoot). When an event fires, it bubbles up to the root, React identifies which component the event target belongs to, creates a SyntheticEvent wrapper, and dispatches it through the React component tree. This is called event delegation, and it is more memory-efficient than attaching thousands of individual listeners.The key behavioral differences from native events: SyntheticEvents are pooled in React 16 and earlier (the event object is reused across handlers, so you cannot access it asynchronously without calling e.persist()). React 17+ removed pooling, so events are regular objects you can access anytime. Also, stopPropagation() on a SyntheticEvent stops propagation in React’s event system but does not stop the native event from propagating — this can cause subtle bugs if you mix React handlers with native addEventListener calls.Follow-up: You add a native event listener via addEventListener in a useEffect, and a React onClick handler on the same element. The stopPropagation in the React handler does not prevent the native listener from firing. Why?Because React 17+ attaches its delegated listener to the root container, not the individual element. When you click the element, the native event propagates through the real DOM first. Your addEventListener handler on the element fires during native propagation. Then the event reaches the root container, React intercepts it, creates a SyntheticEvent, and dispatches it through the React tree. e.stopPropagation() in the React handler stops propagation within React’s virtual event system, but the native propagation already happened.To prevent the native listener from firing, you need to call e.nativeEvent.stopImmediatePropagation() in the React handler, which stops the underlying native event. But this is fragile and usually signals an architectural problem — mixing React and native event handling on the same element is a code smell. Better to handle everything in React or everything natively, not both.Explain event delegation in React. How did React 17 change where events are attached, and why did that matter?
Explain event delegation in React. How did React 17 change where events are attached, and why did that matter?
Strong Answer:
Event delegation is the pattern of attaching a single event listener to a parent element and using
event.target to determine which child triggered it. React uses this internally — instead of attaching an onClick to every button in your app, React attaches one click listener to a container and routes events to the correct component handlers.Before React 17, this single listener was attached to document. In React 17, it moved to the root DOM container (document.getElementById('root')). This change seems minor but solved real production problems.The main issue was apps that gradually migrated to React. If you had a jQuery section and a React section on the same page, React’s listener on document would intercept events from the jQuery section. stopPropagation() called in React would prevent non-React event listeners on document from seeing the event. Moving to the root container isolates React’s event system to its own DOM subtree.It also fixed micro-frontend architectures where multiple React roots coexist. With document-level delegation, two React apps would interfere with each other’s event handling. Container-level delegation means each React root manages its own events independently.In practice, the only code that broke during this migration was code that relied on React events propagating to document — for example, a document.addEventListener('click', closeDropdown) that expected to fire after React’s click handlers. After React 17, the native document listener fires before React’s container-level handler, changing the order.Follow-up: In a performance-critical scenario with 10,000 list items, each needing a click handler, what are the implications of React’s event delegation?Event delegation is actually the ideal solution for this case. React does not attach 10,000 click listeners — it attaches one to the root and routes clicks based on the target. Memory usage is constant regardless of list size, which is exactly what you want for large lists.The caveat is that the routing still involves React identifying which component the click target belongs to, which involves walking up the fiber tree. For 10,000 items this is still O(depth of tree), not O(n), so it is fast. Combined with virtualization (only rendering visible items), the event handling cost is negligible.Where you might see performance issues is if each list item’s click handler is a new arrow function created on every render (onClick={() => handleClick(item.id)}). This does not affect event delegation, but it defeats React.memo on list items because the function reference changes every render. The fix is useCallback or passing the ID to a stable handler that reads it from the event.What is the difference between event.target and event.currentTarget? Describe a scenario where confusing them causes a bug.
What is the difference between event.target and event.currentTarget? Describe a scenario where confusing them causes a bug.
Strong Answer:
event.target is the element that actually triggered the event — the deepest element in the DOM that was clicked, typed in, or interacted with. event.currentTarget is the element that the event handler is attached to. They are the same when you click directly on the element with the handler, but they differ when the event bubbles.Concrete bug scenario: you have a card component with an onClick handler on the outer div. Inside the card there is a title h3 and a description p. The handler reads event.target.dataset.cardId to determine which card was clicked. If the user clicks on the h3, event.target is the h3, which does not have data-card-id — so dataset.cardId is undefined. The handler fails silently or navigates to the wrong route. The fix is to use event.currentTarget.dataset.cardId, which always refers to the div that has the handler (and the data attribute).This comes up constantly in list interfaces. You have a row with onClick on the <tr>, and inside there are <td> elements. event.target could be any <td> or even a <span> inside a <td>. If your handler assumes event.target is the <tr>, you will read wrong data.Another scenario: styling. You have event.target.classList.add('active') in an onClick handler on a container. If the user clicks a child element, you add the class to the child instead of the container. Use event.currentTarget for the element with the handler.Follow-up: How do capture and bubble phases work in React, and when would you use onClickCapture?In native DOM events, there are three phases: capture (event travels from document down to the target), target (event reaches the element), and bubble (event travels back up). React supports both capture and bubble phases through event props: onClick fires during the bubble phase, onClickCapture fires during the capture phase.The capture phase fires before the bubble phase, so onClickCapture on a parent fires before onClick on the child. This is useful when you need a parent to intercept an event before any child sees it.Practical use case: implementing a focus trap in a modal. You add onFocusCapture on the modal container to intercept any focus event before it reaches the target. If the focus target is outside the modal, you redirect focus back inside. Another use case: analytics logging at the app root that captures all clicks before any component’s handler might call stopPropagation. By using the capture phase, your logger always fires regardless of what child handlers do.