Event Delegation
Event Delegation
In vanilla JavaScript, if you have a list of 1,000 buttons and you want to listen for clicks on all of them, attaching 1,000 individual addEventListener('click', ...) functions is terrible for memory and performance.
The standard web development solution is Event Delegation: you attach a single event listener to a parent container (like the <ul> or the document), and let the events from the buttons “bubble up” to the parent, where you inspect event.target to figure out which button was actually clicked.
How React Handles Events
When you write <button onClick={handleClick}> in React, React does not attach an event listener to that specific button in the DOM.
Instead, React automatically implements Event Delegation for you under the hood.
- React attaches exactly one event listener per event type (one for ‘click’, one for ‘keydown’, etc.) at the very root of your React application (the
<div id="root">). (Note: Prior to React 17, it attached them to thedocumentitself). - When you click a button, the browser’s native click event bubbles up to the root
<div>. - React catches the event there.
- React figures out which specific React component was clicked based on its internal mapping.
- React creates a SyntheticEvent (a cross-browser wrapper around the native event) and passes it to your
handleClickfunction.
Why does React do this?
- Performance & Memory: Attaching one listener at the root is significantly faster and consumes far less memory than attaching thousands of individual listeners to specific DOM nodes, especially in large applications where elements are constantly mounting and unmounting.
- Cross-Browser Consistency: By intercepting all events centrally, React normalizes the event object, ensuring that events behave exactly the same way across Chrome, Safari, Firefox, and older browsers.
The React 17 Change (Root vs Document)
Before React 17, React attached all event listeners to document.
This caused major problems if you tried to embed multiple React applications on the same page, or if you mixed React with another library like jQuery. If an inner React app stopped an event from bubbling, the outer React app’s document listener would still fire, causing bizarre bugs.
In React 17, this was changed. React now attaches the delegated event handlers to the root container where your app is rendered (e.g., rootNode.addEventListener()).
This change made it safe to nest React trees created with different versions of React, and made it much easier to integrate React into complex legacy systems.
Interview Questions
Q: How does React’s Event Delegation work for performance?
A: Instead of adding 100 individual click event listeners to 100 different buttons on the page, React attaches exactly one master click listener to the root container. When a click bubbles up to the root, React calculates which Virtual DOM component should handle it and dispatches a Synthetic Event, saving massive amounts of memory.