-
Notifications
You must be signed in to change notification settings - Fork 47.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Global event handlers on document.body (or other containing element) run BEFORE react event handlers #7094
Comments
This is actually beneficial—event delegation is preferable to setting event handlers on every DOM element. IIRC we sometimes add event handlers on specific DOM nodes when absolutely necessary but most of the times we try to reuse the same top-level handler. This is described in the docs:
I would say that generally how React implements events is an implementation detail, and it might change. I wouldn’t suggest trying to prevent a native event handler from a synthetic event handler, or vice versa. If you use events in React, please consider treating it as an encapsulated system. As an escape hatch, you can always sidestep React event system and use class MyComponent extends React.Component {
componentDidMount() {
this.node.addEventListener(...)
}
componentWillUnmount() {
this.node.removeEventListener(...)
}
render() {
return <button ref={node => this.node = node}>Hi</button>
}
} This gives you access to raw DOM APIs and lets you attach handlers to the DOM elements you want. But this comes with a performance penalty so I wouldn’t suggest doing it too often. I hope this helps! |
Thank you for the detailed explanation :-) |
Is it possible to attach a synthetic event handler to |
@gaearon Question about the snippet you posted above: Is it really necessary to manually remove the event listener? If the listener is attached to the Node directly, won't it be removed automatically along with the Node itself upon Unmount? I know this is very anti-pattern, but this got me curious... wouldn't the below snippet suffice?
|
Second the question: Is it possible to attach a synthetic event handler to document.body? I want to make use of its "cross-browser wrapper around the browser’s native event" benefit. My use case is related to keyboard shortcuts handling. I originally use |
I use global events a lot, but there is always hacks required to get the two event systems to sync up. As a suggestion of API, what about, const Popover = props => {
let container
const handleClick = e => {
if (!container.contains(e.target)) {
props.onClose()
}
}
const setRef = r => {
container = r
}
return (
<div ref={setRef} onGlobalClick={handleClick}>
{props.children}
</div>
)
} I had an experiment with attaching events to a root react node, but you have to keep the root node focused. You can see it here, but would definitely like to try something less hacky. |
"hi @gaearon , I could not find the event delegation explanation which you referred in above post in latest react documentation. docs. Any change in that approach? If not, could you direct me to the link where I can find that info."+1 |
Any specific reason why the root listener cannot sit on app's root node, rather than the document? That would IMO make react more self-contained and easier embeddable into contexts that already have their document listeners setup by the time react app initializes. |
We're attaching events to roots in React 17. |
This one is much needed change to React. |
If you want to try it, we released 17 RC yesterday with this change: |
@gaearon after reading the blog, it appears that version 17 and any subsequent 17.(x) will not have any new features. As the blog points out, 17 will be a stepping stone. Does this mean that we can expect version 18 to follow 17 in the not-too-distant future? Or will 17 be the latest major for a considerable time? In a way, this almost "feels" like a 16.(x) version. |
I'd expect 18 to ship within a year or less but my estimates haven't been very accurate historically. |
17.x releases might have some new features though. Just nothing huge. |
Thanks for clarifying @gaearon |
I've discovered that if I add global event handler with
document.body.addEventListener('click', ...)
in a component I cannot stop it withev.stopPropagation()
inside React event handler, because global native event handler runs BEFORE the react one. I'm able to stop it only if I set it withwindow.addEventListener...
I think it's a bad behaviour. Is React setting all synthentic events on window instead of specific DOM elements?
Can it be fixed?
The text was updated successfully, but these errors were encountered: