React 17 delegates events to root instead of document

Chetan Gawai

Chetan Gawai

September 29, 2020

React has been doing event delegation automatically since its first release. It attaches one handler per event type directly at the document node.

Though it improves the performance of an application, many issues have been reported due to the event delegation on the document node.

To demonstrate one of the issues let's take an example of a select dropdown.

CountryDropDown in the below example is a React component for country selection, which would be rendered to a div with id react-root. The react DOM container is wrapped inside a div with id main that has a change event containing stopPropagation().

<!--Div's change event contains stopPropagation()-->
<div id="main">
  <!--Div where react component will be rendered -->
  <div id="react-root"></div>
</div>

class CountryDropDown extends React.Component {
  state = {
    country: '',
  }
  const handleChange = e => {
    this.setState({ country: e.target.value });
  }
  render() {
    return (
      <table class="table table-striped table-condensed">
        <thead>
          <tr>
            <th>Country</th>
            <th>Selected country</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>
              <select value={this.state.country}
                onChange={this.handleChange}
              >
                <option value="">--Select--</option>
                <option value="India">India</option>
                <option value="US">US</option>
                <option value="Dubai">Dubai</option>
              </select>
            </td>
            <td>
              {this.state.country}
            </td>
          </tr>
        </tbody>
      </table>
    );
  }
}
ReactDOM.render(<CountryDropDown />, document.getElementById('react-root'));

Attaching change event to the main div

document.getElementById("main").addEventListener(
  "change",
  function (e) {
    e.stopPropagation();
  },
  false
);

When a country is selected, we cannot see the selected country. Watch this video

to see it in action.

The reason for this unexpected behavior is the onChange event of dropdown which is attached to the document node. The change event of the main div containing e.stopPropagation() prevents the onChange event of dropdown.

To fix such issues, React 17 no longer attaches event handlers at the document level. Instead, it attaches them to the root DOM container into which React tree is rendered.

event delegation

Image is taken from React 17 blog.

Changes in React 17

After changes in React 17, events are attached to the root DOM container into which the React tree is rendered. In our example, onChange event of dropdown would be attached to the div with id react-root. This event would be triggered when any country is selected rendering the expected behavior.

Note

React 17 release candidate can be installed from here.

Check out the earlier discussion on event delegation here and the pull request here.

If this blog was helpful, check out our full blog archive.

Stay up to date with our blogs.

Subscribe to receive email notifications for new blog posts.