> ## 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.

# 04. Handling Events

> Master user interactions including clicks, forms, keyboard, and touch events.

# 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)     |

**HTML:**

```html theme={null}
<button onclick="handleClick()">Click</button>
```

**React:**

```javascript theme={null}
<button onClick={handleClick}>Click</button>
```

***

## Basic Event Handling

### Defining Event Handlers

```javascript theme={null}
function Button() {
  // Method 1: Separate function (recommended for complex logic)
  const handleClick = () => {
    console.log('Button clicked!');
  };

  return <button onClick={handleClick}>Click Me</button>;
}

// Method 2: Inline function (ok for simple cases)
function Button() {
  return (
    <button onClick={() => console.log('Clicked!')}>
      Click Me
    </button>
  );
}
```

<Tip>
  **Best Practice**: Define handlers as separate functions for:

  * Better readability
  * Easier debugging
  * Potential performance benefits with `useCallback`
</Tip>

### 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.

```javascript theme={null}
// ❌ WRONG - Parentheses () mean "call this right now!"
// handleClick runs during render, not on click.
// If handleClick calls setState, you get an infinite render loop.
<button onClick={handleClick()}>Click</button>

// ✅ CORRECT - Pass the function reference (no parentheses)
// React will call it later when the user actually clicks.
<button onClick={handleClick}>Click</button>

// ✅ CORRECT - Wrap in an arrow function if you need to pass arguments
<button onClick={() => handleClick()}>Click</button>
```

<Warning>
  **Practical pitfall**: If you see your handler firing on every render (or your app freezing in an infinite loop), check for accidental function invocation with `()`. This is the #1 debugging timesink for React beginners.
</Warning>

***

## The Event Object

React wraps native browser events in a **SyntheticEvent** for cross-browser compatibility.

```javascript theme={null}
function Form() {
  const handleSubmit = (event) => {
    event.preventDefault(); // Stop form from refreshing page
    console.log('Form submitted');
  };

  const handleChange = (event) => {
    console.log('Target element:', event.target);
    console.log('Input value:', event.target.value);
    console.log('Input name:', event.target.name);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input 
        name="email" 
        onChange={handleChange}
        placeholder="Email"
      />
      <button type="submit">Submit</button>
    </form>
  );
}
```

### 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.

```javascript theme={null}
function ProductList({ products }) {
  const handleDelete = (productId) => {
    console.log('Deleting product:', productId);
  };

  const handleEdit = (productId, currentName) => {
    console.log('Editing:', productId, currentName);
  };

  return (
    <ul>
      {products.map(product => (
        <li key={product.id}>
          {product.name}
          {/* Arrow function wrapper */}
          <button onClick={() => handleDelete(product.id)}>
            Delete
          </button>
          <button onClick={() => handleEdit(product.id, product.name)}>
            Edit
          </button>
        </li>
      ))}
    </ul>
  );
}
```

### Accessing Event AND Custom Arguments

```javascript theme={null}
const handleClick = (productId, event) => {
  event.preventDefault();
  console.log('Product:', productId);
  console.log('Button:', event.target);
};

<button onClick={(e) => handleClick(product.id, e)}>
  Click
</button>
```

***

## Mouse Events

```javascript theme={null}
function InteractiveBox() {
  const [isHovered, setIsHovered] = useState(false);

  return (
    <div
      style={{
        backgroundColor: isHovered ? 'lightblue' : 'lightgray',
        padding: '20px',
        cursor: 'pointer'
      }}
      onClick={() => console.log('Clicked!')}
      onDoubleClick={() => console.log('Double clicked!')}
      onMouseEnter={() => setIsHovered(true)}
      onMouseLeave={() => setIsHovered(false)}
      onMouseMove={(e) => console.log(`Position: ${e.clientX}, ${e.clientY}`)}
      onContextMenu={(e) => {
        e.preventDefault(); // Prevent right-click menu
        console.log('Right clicked!');
      }}
    >
      Hover and Click Me
    </div>
  );
}
```

### 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

```javascript theme={null}
function SearchInput() {
  const [query, setQuery] = useState('');

  const handleKeyDown = (event) => {
    // Check for specific keys
    if (event.key === 'Enter') {
      console.log('Searching for:', query);
      // Perform search
    }
    
    if (event.key === 'Escape') {
      setQuery('');
    }

    // Keyboard shortcuts with modifiers
    if (event.ctrlKey && event.key === 'k') {
      event.preventDefault();
      console.log('Ctrl+K pressed - Open command palette');
    }
  };

  return (
    <input
      type="text"
      value={query}
      onChange={(e) => setQuery(e.target.value)}
      onKeyDown={handleKeyDown}
      placeholder="Search... (Enter to submit, Esc to clear)"
    />
  );
}
```

### 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

```javascript theme={null}
function App() {
  useEffect(() => {
    const handleGlobalKeyDown = (event) => {
      // Ctrl/Cmd + S to save
      if ((event.ctrlKey || event.metaKey) && event.key === 's') {
        event.preventDefault();
        saveDocument();
      }
      
      // Ctrl/Cmd + Shift + P for command palette
      if ((event.ctrlKey || event.metaKey) && event.shiftKey && event.key === 'P') {
        event.preventDefault();
        openCommandPalette();
      }
    };

    window.addEventListener('keydown', handleGlobalKeyDown);
    return () => window.removeEventListener('keydown', handleGlobalKeyDown);
  }, []);

  return <div>...</div>;
}
```

***

## Form Events

### Input Change Event

```javascript theme={null}
function FormExample() {
  const [formData, setFormData] = useState({
    name: '',
    email: '',
    agree: false
  });

  const handleChange = (event) => {
    const { name, value, type, checked } = event.target;
    setFormData(prev => ({
      ...prev,
      [name]: type === 'checkbox' ? checked : value
    }));
  };

  return (
    <form>
      <input
        name="name"
        value={formData.name}
        onChange={handleChange}
      />
      <input
        name="email"
        type="email"
        value={formData.email}
        onChange={handleChange}
      />
      <input
        name="agree"
        type="checkbox"
        checked={formData.agree}
        onChange={handleChange}
      />
    </form>
  );
}
```

### Focus Events

```javascript theme={null}
function FocusExample() {
  const [isFocused, setIsFocused] = useState(false);

  return (
    <div>
      <input
        onFocus={() => setIsFocused(true)}
        onBlur={() => setIsFocused(false)}
        style={{
          borderColor: isFocused ? 'blue' : 'gray',
          outline: 'none'
        }}
      />
      {isFocused && <p>Input is focused!</p>}
    </div>
  );
}
```

***

## Event Propagation

Events bubble up through the DOM tree. You can stop this behavior.

```javascript theme={null}
function Card() {
  const handleCardClick = () => {
    console.log('Card clicked');
  };

  const handleButtonClick = (event) => {
    event.stopPropagation(); // Prevents handleCardClick from firing
    console.log('Button clicked');
  };

  return (
    <div onClick={handleCardClick} className="card">
      <h2>Card Title</h2>
      <button onClick={handleButtonClick}>
        Click Me (doesn't trigger card click)
      </button>
    </div>
  );
}
```

### Event Capturing

React also supports the capture phase (rarely needed):

```javascript theme={null}
<div onClickCapture={() => console.log('Capture phase')}>
  <button onClick={() => console.log('Bubble phase')}>
    Click
  </button>
</div>
// Logs: "Capture phase" then "Bubble phase"
```

***

## Touch Events (Mobile)

```javascript theme={null}
function SwipeableCard() {
  const [touchStart, setTouchStart] = useState(null);
  const [touchEnd, setTouchEnd] = useState(null);

  const minSwipeDistance = 50;

  const onTouchStart = (e) => {
    setTouchEnd(null);
    setTouchStart(e.targetTouches[0].clientX);
  };

  const onTouchMove = (e) => {
    setTouchEnd(e.targetTouches[0].clientX);
  };

  const onTouchEnd = () => {
    if (!touchStart || !touchEnd) return;
    
    const distance = touchStart - touchEnd;
    const isLeftSwipe = distance > minSwipeDistance;
    const isRightSwipe = distance < -minSwipeDistance;
    
    if (isLeftSwipe) console.log('Swiped left');
    if (isRightSwipe) console.log('Swiped right');
  };

  return (
    <div
      onTouchStart={onTouchStart}
      onTouchMove={onTouchMove}
      onTouchEnd={onTouchEnd}
      className="swipeable-card"
    >
      Swipe me!
    </div>
  );
}
```

***

## Drag and Drop Events

```javascript theme={null}
function DragDropExample() {
  const [draggedItem, setDraggedItem] = useState(null);
  const [items, setItems] = useState(['Item 1', 'Item 2', 'Item 3']);

  const handleDragStart = (e, index) => {
    setDraggedItem(index);
    e.dataTransfer.effectAllowed = 'move';
  };

  const handleDragOver = (e) => {
    e.preventDefault();
    e.dataTransfer.dropEffect = 'move';
  };

  const handleDrop = (e, dropIndex) => {
    e.preventDefault();
    const newItems = [...items];
    const [removed] = newItems.splice(draggedItem, 1);
    newItems.splice(dropIndex, 0, removed);
    setItems(newItems);
    setDraggedItem(null);
  };

  return (
    <ul>
      {items.map((item, index) => (
        <li
          key={item}
          draggable
          onDragStart={(e) => handleDragStart(e, index)}
          onDragOver={handleDragOver}
          onDrop={(e) => handleDrop(e, index)}
          style={{
            opacity: draggedItem === index ? 0.5 : 1,
            cursor: 'move'
          }}
        >
          {item}
        </li>
      ))}
    </ul>
  );
}
```

***

## Event Handler Pitfalls

<Warning>
  **Pitfall 1 -- Inline arrow functions in lists cause unnecessary re-renders**: When you write `onClick={() => handleDelete(item.id)}` inside a `.map()`, a new function is created for every item on every render. If the child component is wrapped in `React.memo`, this defeats memoization because the `onClick` prop is always a new reference. For large lists, extract the handler or use `useCallback`.

  **Pitfall 2 -- SyntheticEvent pooling (React 16 and earlier)**: In older React versions, event objects were pooled and reused. Accessing `event.target` inside a `setTimeout` or async function returned null because the event had been recycled. React 17+ eliminated pooling, but you may encounter this in legacy codebases. The fix was `event.persist()`.

  **Pitfall 3 -- stopPropagation hiding bugs**: Using `event.stopPropagation()` prevents an event from reaching parent handlers. This can silently break features like a "click outside to close" listener on the document, because the click event never reaches the document. Use it sparingly and document why.

  **Pitfall 4 -- Double-click interfering with single click**: If you listen to both `onClick` and `onDoubleClick` on the same element, a double-click fires two `onClick` events before the `onDoubleClick`. To distinguish between them, use a timer: wait \~250ms after a click before acting, and cancel it if a double-click arrives.
</Warning>

***

## 🎯 Practice Exercises

<Accordion title="Exercise 1: Like Button with Double-Click to Super Like">
  ```javascript theme={null}
  function LikeButton() {
    const [likes, setLikes] = useState(0);
    const [superLiked, setSuperLiked] = useState(false);

    const handleClick = () => {
      if (!superLiked) {
        setLikes(prev => prev + 1);
      }
    };

    const handleDoubleClick = () => {
      setSuperLiked(true);
      setLikes(prev => prev + 10);
    };

    return (
      <button
        onClick={handleClick}
        onDoubleClick={handleDoubleClick}
        style={{
          backgroundColor: superLiked ? 'gold' : 'pink',
          border: 'none',
          padding: '10px 20px',
          borderRadius: '20px',
          cursor: 'pointer'
        }}
      >
        {superLiked ? '⭐' : '❤️'} {likes}
      </button>
    );
  }
  ```
</Accordion>

<Accordion title="Exercise 2: Keyboard-Controlled Character">
  ```javascript theme={null}
  function Character() {
    const [position, setPosition] = useState({ x: 50, y: 50 });
    const step = 10;

    useEffect(() => {
      const handleKeyDown = (e) => {
        switch (e.key) {
          case 'ArrowUp':
          case 'w':
            setPosition(p => ({ ...p, y: Math.max(0, p.y - step) }));
            break;
          case 'ArrowDown':
          case 's':
            setPosition(p => ({ ...p, y: Math.min(90, p.y + step) }));
            break;
          case 'ArrowLeft':
          case 'a':
            setPosition(p => ({ ...p, x: Math.max(0, p.x - step) }));
            break;
          case 'ArrowRight':
          case 'd':
            setPosition(p => ({ ...p, x: Math.min(90, p.x + step) }));
            break;
        }
      };

      window.addEventListener('keydown', handleKeyDown);
      return () => window.removeEventListener('keydown', handleKeyDown);
    }, []);

    return (
      <div style={{
        position: 'relative',
        width: '300px',
        height: '300px',
        border: '1px solid black'
      }}>
        <div style={{
          position: 'absolute',
          left: `${position.x}%`,
          top: `${position.y}%`,
          width: '20px',
          height: '20px',
          backgroundColor: 'blue',
          borderRadius: '50%',
          transition: 'all 0.1s'
        }} />
        <p>Use WASD or Arrow keys to move</p>
      </div>
    );
  }
  ```
</Accordion>

<Accordion title="Exercise 3: Click Outside to Close">
  ```javascript theme={null}
  function Dropdown() {
    const [isOpen, setIsOpen] = useState(false);
    const dropdownRef = useRef(null);

    useEffect(() => {
      const handleClickOutside = (event) => {
        if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
          setIsOpen(false);
        }
      };

      document.addEventListener('mousedown', handleClickOutside);
      return () => document.removeEventListener('mousedown', handleClickOutside);
    }, []);

    return (
      <div ref={dropdownRef} className="dropdown">
        <button onClick={() => setIsOpen(!isOpen)}>
          {isOpen ? 'Close' : 'Open'} Menu
        </button>
        {isOpen && (
          <ul className="dropdown-menu">
            <li>Option 1</li>
            <li>Option 2</li>
            <li>Option 3</li>
          </ul>
        )}
      </div>
    );
  }
  ```
</Accordion>

***

## 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)         |

<Card title="Next Steps" icon="arrow-right">
  In the next chapter, you'll learn about **Lists & Keys** — efficiently rendering collections of data!
</Card>

***

## Interview Deep-Dive

<AccordionGroup>
  <Accordion title="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.
  </Accordion>

  <Accordion title="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.
  </Accordion>

  <Accordion title="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.
  </Accordion>
</AccordionGroup>
