admin管理员组

文章数量:1180476

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>);

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>);
Share Improve this question edited Jun 11, 2024 at 15:40 greim 9,4276 gold badges35 silver badges35 bronze badges asked Aug 30, 2020 at 23:22 Jeff DemancheJeff Demanche 7641 gold badge7 silver badges25 bronze badges
Add a comment  | 

2 Answers 2

Reset to default 29

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.

For anyone using functional components, here is a solution, taking inspiration from @johndodo

If your onWheel does not change over time

function MyComponent {
  const onWheel = useCallback(
    (e) => {
      console.log(e);
      e.preventDefault();
    },
    [],
  );

  const divRefCallback = useCallback(
    (node) => {
      console.log(node);
      if (node == null) {
        return;
      }
      node.addEventListener('wheel', onWheel, { passive: false });
    },
    [onWheel],
  );

  return <div ref={divRefCallback}>...</div>;
}

The reason to use a useCallback for divRef instead of useRef + useEffect is this.

If your onWheel changes over time

e.g. if it depends on a state.

This requires some extra work because you need to remove the old event listener.

function MyComponent {
  const [x, setX] = useState(0);

  const onWheel = useCallback(
    (e) => {
      console.log(e);
      console.log(x);
      e.preventDefault();
    },
    [x],
  );

  const divRef = useRef(null);
  const divRefCallback = useCallback(
    (node) => {
      console.log(node);
      if (node == null) {
        if (divRef.current != null) {
          /* This happens when the <div> component tries to detach the old divRefCallback.
           * Since we are inside a `useCallback`, the `onWheel` callback being remove is still the old one here.
           */
          divRef.current.removeEventListener('wheel', onWheel, { passive: false });
        }
        return;
      }
      divRef.current = node;
      node.addEventListener('wheel', onWheel, { passive: false });
    },
    [onWheel],
  );

  return <div ref={divRefCallback}>...</div>;
}

本文标签: javascriptReact onWheel handler can39t preventDefault because it39s a passive event listenerStack Overflow