React 17 stops event bubbling in scroll event


React bubbled all its events before React 17, even the ones that the browser doesn’t bubble by default. This has caused some weird behavior #19156 #15723 with scroll event.

To demonstrate one of the issues let’s take a simple example.

A scrollable div containing some text is wrapped by parent div. The parent div has onScroll event attached to it which changes the background color to pink.

class App extends React.Component {
  parentNode = React.createRef();

  handleScroll = (e) => {
    if (!ticking) {
      window.requestAnimationFrame(() => {
        //Change background color to pink
        this.parentNode.current.style.background = '#E22C7E';
        ticking = false;
      });
      ticking = true;
    }
  }

  render() {

    return (
      <div className="wrapper">
        {/* Parent div */}
        <div
          ref={this.parentNode}
          className="parent"
          onScroll={this.handleScroll}
          id="parent"
        >
          <b>I am parent</b>
          {/* Scrollable child div */}
          <div className="child" id="child">
            <b>I am child</b><br />
            Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged.
          </div>
          <div style={{ height: '1000px' }}/>
        </div>
      </div>
    )
  }
}

When we scroll in the child div, we can see that the background color changes to pink as shown in the below gif.

This is strange. The reason behind this is that the scroll event of child div bubbles, calling the onScroll event of the parent which changes the background color.

Before

With React 16 or lower, the scrolling issue could be fixed by adding a condition as below -

   handleScroll = (e) => {
    //Add condition
    if (e.target === e.currentTarget) {
      if (!ticking) {
        window.requestAnimationFrame(() => {
           //Change background to pink
           this.parentNode.current.style.background = '#E22C7E';
           ticking = false;
        });
        ticking = true;
      }
    }
  }

This approach is not feasible if the hierarchy increases.

After

To avoid such issues, React 17 has stopped bubbling for a scroll event. It now aligns with the browser scroll event.

With React 17 changes, scrolling the paragraph in our example would not change the background color to pink as shown in the below gif which fixes our issue.

Check out the pull request to learn more.