Search code examples
javascriptreactjsdomdom-events

React onWheel handler can't preventDefault because it's a passive event listener


I'm trying to override Ctrl+scroll behavior on a component, but it's not working with the error [Intervention] Unable to preventDefault inside passive event listener due to target being treated as passive. See <URL>. I think I'm okay with using an active listener, so is there a way to specify that through React? Note that I need to access and modify state within onWheel.

  const onWheel = (e: React.WheelEvent): void => {
    if (e.altKey) {
      e.preventDefault();
      // Error
    } else if (e.ctrlKey) {
      e.preventDefault();
      // Error
    }
  };

...

  return (<div className={styles["workspace"]} onWheel={onWheel}>
    stuff
  </div>);

Solution

  • A bit late, but maybe it helps someone else.

    The problem is that React uses passive event handlers by default with wheel, touchstart and touchmove events - in other words, you can't call stopPropagation within them.

    If you want to use non-passive event handlers, you need to use refs and add/remove event handler manually, like this:

    class MyComponent extends React.Component {
      myRef = React.createRef();
    
      componentDidMount() {
        // IMPORTANT: notice the `passive: false` option
        this.myRef.current.addEventListener('wheel', this.handleWheel, { passive: false });
      }
    
      componentWillUnmount() {
        this.myRef.current.removeEventListener('wheel', this.handleWheel, { passive: false });
      }
    
      handleWheel = (e) => {
        e.stopPropagation();
        // ...
      }
    
      // ...
    }
    

    Should be similar with hooks.