What is Event Pooling in React?

Event pooling was a performance optimization technique used in older versions of React (React 16 and earlier) to improve application speed by reusing event objects. This article explains how event pooling works, the common issues it caused for developers—particularly when dealing with asynchronous code—and why the React team ultimately decided to remove it in React 17.

Understanding the Concept of Event Pooling

In React, event handlers do not receive native browser events. Instead, they receive instances of SyntheticEvent, which is a cross-browser wrapper around the browser’s native event.

Historically, creating a new SyntheticEvent object for every single user interaction put a strain on garbage collection, especially in older web browsers. To solve this, React implemented event pooling. Instead of destroying and recreating event objects constantly, React kept a “pool” of event objects.

When an event was triggered, React would: 1. Retrieve an event object from the pool. 2. Populate its properties with the current event data. 3. Execute your event handler function. 4. Immediately clear (nullify) all properties on the event object. 5. Return the object to the pool for future reuse.

The Problem with Asynchronous Access

While event pooling was great for memory management, it introduced a major pain point for developers trying to access event properties inside asynchronous code.

Because React cleared the event object immediately after the synchronous event handler finished executing, any attempt to access the event object inside a setTimeout, a Promise, or an asynchronous state update would result in null or undefined values.

Consider this example, which caused bugs in React 16:

function MyComponent() {
  const handleClick = (event) => {
    // This works synchronously
    console.log(event.target.value); 

    // This fails in React 16 and earlier
    setTimeout(() => {
      console.log(event.target.value); // Error: Cannot read property 'value' of null
    }, 1000);
  };

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

To bypass this issue, developers had to call event.persist(). This method told React to remove the specific event from the pool, allowing its properties to remain intact so they could be accessed asynchronously.

Why Event Pooling Was Removed in React 17

With the release of React 17, the React team officially removed event pooling.

Modern browsers have highly optimized garbage collection engines, meaning the minor performance benefits of recycling event objects no longer justified the developer confusion and bugs caused by pooled events. Today, React creates a new event object for every event just like native JavaScript does, allowing you to access event properties inside asynchronous functions without ever needing to call event.persist().