React: Events propagate to nested components after stopPropagation()

Created on 14 Jun 2014  ·  17Comments  ·  Source: facebook/react

We're using react-bootstrap modals in a site with full page rendering, so we have nested React rendering. As of d853c8568ed870340984b8a86281158f888c8593 events are propagated to ancestors, but calls to event.stopPropagation() on the inner component hierarchy are not respected by the outer component hierarchy.

Complicating matters, the SyntheticEvent is created afresh for dispatch to each component hierarchy and there is no stoppedPropagation equivalent to nativeEvent.defaultPrevented to be called in the SyntheticEvent constructor. So even if forEachEventDispatch (EventPluginUtils.js) checked event.isPropagationStopped() in the single callback case, it would still return false.

Tracking the propagationStopped status through a return value of ReactEventEmitter.handleTopLevel looks tricky too given EventPluginHub.extractEvents may map a single nativeEvent to multiple synthetic events.

The easiest fix may just be to set a custom property on the nativeEvent object in SyntheticEvent.stopPropagation to check in the constructor, then check event.isPropagationStopped() in the single callback case of forEachEventDispatch.

Most helpful comment

For the record, I ran into this problem without doing anything exotic.

I've got a CRUD-style entity list table where each row has an onClick handler to "choose" the entity the row represents. But in one of the row's cells, there are control buttons with their own onClick handlers.

Now when I click on one of the buttons under the table row, the table row's onClick gets triggered before the button's!

I tried circumventing the problem by setting up a native browser event for the button component, but now any click anywhere seems to trigger just about everything on the page, even though I'm calling all the stop propagation methods I could find.

How can this still be a problem after two years? Why does React hijack event handling to begin with?

All 17 comments

@lrowe Prop_o_gation should be Prop_a_gation

Thanks @sompylasar, spelling fixed.

Hi there, it's been around 1.5 years passed, any plan on this issue? There are several cases in our team that we need to prevent the event bubbling of inner components. The workaround for us is painful: to drill down the target element and adding addEventListener to stop propagation there, then we have to care about the clean up, remove the listener when unmount.

Not really, unfortunately. We don't have a good model for how events for nested trees should interact. Synthetic events and native events are two different concepts – for example, a native keyup event could result in a synthetic onChange event – but if you call preventDefault on the onChange event, should that prevent the keyup? We dispatch the native event to each render tree and form the synthetic events separately in each tree right now, which we'd probably need to change to fix this properly. I'm afraid that any short-term solution here would be a patch that solves only a few cases and makes the code harder to reason about; we probably need a holistic rethinking of this problem to make a good solution here, and it's not something that we encounter very much because we're much more focused on React in a single render tree.

For the record, I ran into this problem without doing anything exotic.

I've got a CRUD-style entity list table where each row has an onClick handler to "choose" the entity the row represents. But in one of the row's cells, there are control buttons with their own onClick handlers.

Now when I click on one of the buttons under the table row, the table row's onClick gets triggered before the button's!

I tried circumventing the problem by setting up a native browser event for the button component, but now any click anywhere seems to trigger just about everything on the page, even though I'm calling all the stop propagation methods I could find.

How can this still be a problem after two years? Why does React hijack event handling to begin with?

Getting the same problem. I'm in a somewhat exotic environment though (overloading Gmail via a Chrome Extension), but the event propagation should stop all the same. I'm sure there was a good reason for hijacking the event system and creating SyntheticEvents, but this kinda takes away from something I really like about React (the fact that it always has a "top-hatch" to let you plug-n-play it in any environment).

Hi, I have a parent div and a child div, both handle onClick. If the mouse is on the child, I am calling event.stopPropogation but it is not working... This works fine in regular html. but not in react.

I have same problem. And I used a third-party component, Which means binding vanila event and then stop propagation is not feasible.

I have the same problem.

Sad

... same problem here... this is really annoying.

Is there a plan to fix this issue in the next version of React?

I stumbled accross this aswell. really annoying. @spicyj, any news?

It seems react-dropzone is also suffering from this one.

Portals introduced in React 16 let you unite several DOM trees into a single conceptual React tree. If I understand correctly, this should help address the original problem described in the post, as event bubbling works respects the React hierarchy in portals.

As @sophiebits noted in https://github.com/facebook/react/issues/1691#issuecomment-153951263, it is not clear what a generic fix would be like. Comments like “+1” and “Sad” don’t bring us closer to solving that problem.

I am closing this issue as I believe a reasonable workaround (portals) exists in React 16. Libraries providing modals should migrate to using portals.

If you are experiencing this problem but have a different use case, please file a new issue and describe it in more detail, preferably with an example. Unfortunately most other comments in this thread are too brief or lacking details.

We're fixing a subset of these problems in React 17 — by moving native React event listeners from document to the root. Portals are still recommended if you use one copy of React though.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

UnbearableBear picture UnbearableBear  ·  3Comments

krave1986 picture krave1986  ·  3Comments

zpao picture zpao  ·  3Comments

jimfb picture jimfb  ·  3Comments

kocokolo picture kocokolo  ·  3Comments