8. Handling Events

Chapter 8: Handling Events in React

Interactivity in web applications stems from user events—clicks, keystrokes, form submissions, and pointer drags. Learn how React normalizes browser events through the SyntheticEvent wrapper, optimizes memory with root delegation, and manages argument passing cleanly.

1. How React Events Differ from Native HTML Events

While React event syntax resembles traditional HTML attributes, there are three critical syntactic and behavioral differences:

CriteriaNative HTML (DOM Level 0)React Event Handlers
Naming ConventionLowercase: onclick, onsubmitcamelCase: onClick, onSubmit
Handler ValueString of executable code: "doSomething()"Function reference or arrow callback: {doSomething}
Preventing DefaultCan return false to stop default behaviorMust explicitly invoke e.preventDefault()

Code Demonstration: Preventing Form Navigation

function SearchForm() {
  function handleSubmit(event) {
    // Stop the browser from executing a full page refresh / GET submission
    event.preventDefault();
    console.log("Form submitted via AJAX without reloading!");
  }

  return (
    <form onSubmit={handleSubmit}>
      <input type="text" placeholder="Search tutorials..." />
      <button type="submit">Search</button>
    </form>
  );
}

2. The SyntheticEvent Architecture & Delegation

When an event fires, React passes a SyntheticEvent object to your handler function. This object is a cross-browser wrapper conforming to the W3C spec, guaranteeing that properties likee.key, e.clientX, and e.target behave identically across Chrome, Safari, Firefox, and Edge.

🔍 Event Delegation at the Root (`#root`)

Imagine a table with 5,000 rows, each with an edit button. Attaching 5,000 native browser event listeners (via addEventListener) would consume substantial browser memory and slow down garbage collection.

React avoids this entirely. In modern React (v17+), React attaches one single native listener per event typeto your root container DOM node (document.getElementById('root')). When a click occurs on any button, it bubbles up to #root, where React maps the event to the appropriate component handler!

3. Passing Parameters & Arguments to Event Handlers

In real-world applications, you frequently need to pass custom IDs or row objects to an event handler (such as deleting an item from a shopping cart). There are two common approaches:

Approach A: Inline Arrow Function (Standard Practice)

function UserList({ users, onDeleteUser }) {
  return (
    <ul>
      {users.map(user => (
        <li key={user.id}>
          {user.name}
          {/* We pass an anonymous arrow function that invokes onDeleteUser with the ID */}
          <button onClick={() => onDeleteUser(user.id)}>
            Delete
          </button>
        </li>
      ))}
    </ul>
  );
}

Approach B: Curried / Higher-Order Function

Alternatively, define a function that returns an event handler:

function TaskManager() {
  // Returns a closure capturing the task ID
  const handleRemove = (taskId) => (event) => {
    console.log("Removing task:", taskId);
  };

  return <button onClick={handleRemove(42)}>Remove Task #42</button>;
}
⚠️ The Infinite Loop Anti-Pattern

Never write <button onClick={deleteUser(id)}>! Because the parenthesis () follow the function name, JavaScript invokes deleteUser immediately during the component's render pass. If deleteUser updates state, this immediately causes a re-render, which calls it again, resulting in the dreaded: Too many re-renders. React limits the number of renders to prevent an infinite loop.

4. Event Bubbling & `e.stopPropagation()`

Events in React follow the standard DOM bubbling phase: an event triggered on a nested child bubbles upward through every parent element.

If you have a card component with an overall onClick that navigates to a details page, and a nested "Favorite" button inside it, clicking the Favorite button will also trigger the parent Card click unless you stop propagation:

function InteractiveCard({ onCardClick, onFavoriteClick }) {
  return (
    <div className="card" onClick={onCardClick}>
      <h3>Product Title</h3>
      
      <button 
        className="favorite-btn" 
        onClick={(e) => {
          // Prevent the click event from bubbling up to the outer div.card!
          e.stopPropagation();
          onFavoriteClick();
        }}
      >
        ❤️ Favorite
      </button>
    </div>
  );
}

Frequently Asked Questions (FAQ)

Why do we pass a function reference (onClick={handleClick}) instead of calling it (onClick={handleClick()})?

If you write onClick={handleClick()}, JavaScript executes the function immediately during the render phase and assigns its return value to the onClick handler. This often causes unintended infinite re-render loops if the function updates state. Passing the reference (onClick={handleClick}) ensures the function only executes when the user actually triggers the event.

What is the difference between e.target and e.currentTarget in React?

e.target refers to the specific DOM element that dispatched the event (e.g., an icon inside a button), whereas e.currentTarget refers to the DOM element to which the React event listener is actually attached (the button itself).

What happened to Event Pooling in modern React?

In React 16 and older versions, React pooled SyntheticEvent objects to optimize memory on low-end devices, meaning event fields were nulled out immediately after the handler executed unless e.persist() was called. Starting in React 17, event pooling was completely removed, allowing you to safely read event properties inside asynchronous callbacks without calling e.persist().

How does React manage event delegation across the DOM?

Rather than attaching separate native DOM event listeners to every individual element (which consumes significant memory), React attaches a single listener for each event type to the root DOM container (the div#root element where ReactDOM.createRoot was called). It intercepts events as they bubble up and dispatches them to the corresponding React components.

Explore Related Tools

Hand-picked utilities and calculators related to this tool.

Related Tools