react-reverse-portal icon indicating copy to clipboard operation
react-reverse-portal copied to clipboard

Seems reverse portals are not passing events up the tree

Open pie6k opened this issue 5 years ago • 6 comments

If I have some eg onClick events on component inside portal - this event will not be detected if used with reverse portal.

Eg. InPortal is rendered at the root level of the app

Then OutPortal is used inside some container that has it's own click events.

In such case - click events are only passed up directly to the root element, skipping container of OutPortal.

pie6k avatar Aug 04 '20 14:08 pie6k

Interesting! I hadn't thought about this. For normal portals in React that's the correct & intended behaviour (see https://reactjs.org/docs/portals.html#event-bubbling-through-portals), but I'm not totally sure the same reasoning applies here.

In normal portals, the DOM-tree position is an implementation detail, and the React-tree position is the 'real' meaningful location of the component.

With reverse portals, I think you're right that the meaningful location is where the component is rendered though, and that's where events should fire. For similar reasons, you should never hear events from an InPortal component that's not rendered to any OutPortal anywhere on the page.

I'd be open to PRs to look at this. I'm not sure how hard this would be to change, or if there'll be other side-effects to doing so, so it'll take some careful investigation.

pimterry avatar Aug 04 '20 15:08 pimterry

Do you have any idea on how we can try to achieve that goal? At the moment I don't have in mind any solution but rewrite the createPortal at all in some way...

alessandro308 avatar Mar 02 '22 15:03 alessandro308

I think we can do it without changing how the rendering happens, just by hooking into the events system, in theory at least.

We need to somehow listen for events that bubble up to the top of the portal contents, and then manually stop those propagating there through React (so they don't go back to the InPortal) and manually send them to the OutPortal parent instead.

As an example solution, what happens if we do something like:

  • Listen for all raw DOM events on the portal output DOM node (https://stackoverflow.com/a/48388878/68051)
  • Every time an event is fired:
    • Call event.stopPropagation() on the event
    • Call outPortalParentNode.dispatchEvent(event) to send the raw DOM event to the portal parent DOM node instead to continue propagating from there.

In theory it sounds like that would work to me, but I don't know much about the internals of how React tracks & handles events like this, so it needs a bunch of research & testing.

pimterry avatar Mar 02 '22 16:03 pimterry

I actually made a POC and it works... Now we need to find a way to make it with all the possible events... The cons of my implementation is the extra div I need to add as a parent catcher

alessandro308 avatar Mar 04 '22 15:03 alessandro308

Do you have any progress here? I see the open PR, but there is nothing has changed since last year

dimkajasons avatar Feb 17 '23 12:02 dimkajasons

Not really, no, and I'm afraid I don't have much time to investigate this myself right now. PRs very welcome, reading through the code & comments on the existing stalled PR #34 should be a good intro for anybody interested in implementing this.

pimterry avatar Feb 21 '23 12:02 pimterry