Mirror of facebook react#17651 #1
Open
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Mirror of facebook react#17651
Note: This API is intentionally meant to be a low-level way of creating events and assigning listeners to them. It's meant to be verbose so larger building blocks can be created on top of them.
This PR is an alternative solution and system to that of my other PR: facebook#17508. Specifically, based off feedback internally, I've tried to tackle some of the problems that were brought up with the
createListener
approach in facebook#17508:createListener
largely depended on events being registered in the commit phase, meaning that there would likely be issues around needing to flush more to ensure we register DOM events. The new approach enforces all events are registered unconditionally via hooks in the render phase, mitigating this issue.createListener
allowed for listeners to update and change their properties between renders, which again is problematic for performance and would also require more flushes to ensure we have committed the latest version of each listener.createListener
had a complex diffing process to ensure we stored the latest listeners, but this meant that the process had additional overhead and memory usage – which is no longer the case with this PR.createListener
required listeners to be put on nodes via thelisteners
prop. Furthermore, it required using arrays to combine multiple listeners, which some felt was not idealistic and might be confusing during debugging as to which listeners occured at which stages. Also, there was general dislike to introducing another internal prop – as it would mean we'd have to first forbidlisteners
and wait for React 17 to introduce these APIs, as they might be used in the wild for other reasons (custom elements).createListener
didn't provide an idiomatic way to conditionally add/remove root events (they're called delegated events in this new PR). With the new approach, there's a streamlined approach to ensure this is easier to do, and by default, no root events can be added unconditonally, which is a code-smell and a good cause of memory leaks/performance issues.Taking the above points into consideration, the design of this new event system aims at bringing the same capabilities as described in facebook#17508 whilst also providing some other nice features, that should allow for bigger event sub-systems to be built on top.
ReactDOM.useEvent
This hook allows for the registration to a native DOM event, similar to that of
addEventListener
on the web.useEvent
takes a given eventtype
and registers it to the DOM then returns an object unique to that event that allows listeners to be attached to their targets in an effect or another event handler. The object provides three different methods to setup and handle listeners:setListener(target: document | element, listener: Event => void)
set a listener to be active for a given DOM node. The node must be a DOM node managed by React or it can be thedocument
node for delegation purposes.deleteListener(target: document | element)
delete the listener for the given DOM nodeclear()
remove all listenersThe hook takes three arguments:
type
the DOM event to listen tooptions
an optional object allowing for additional properties to be defined on the event listener. The options are:passive
provide an optional flag that tells the listener to register a passive DOM event listener or an active DOM event listenercapture
provide an optional flag that tells thge listener callback to fire in the capture phase or the bubble phasepriority
provide an optional Scheduler priority that allows React to correct schedule the listener callback to fire at the correct priority.Note
For propagation, the same rules of
stopPropgation
andstopImmediatePropgation
apply to these event listeners. These methods are actually monkey-patched, as we use the actual native DOM evnets with this system and API, rather than Synthetic Events.currentTarget
andeventPhase
are also respectfully monkey-patched to co-ordinate and align with the propagation system involved internally within React.Furthermore, all event listeners are passive by default. If is desired to called
event.preventDefault
on an event listener, then the event listener should be made active via thepassive
option.Examples
An example of a basic clickable button:
If you want to listen to events that are delegated to the document, you can do that:
If you wanted to extract the verbosity out of this into a custom hook, then it's possible to do so:
A more complex button that tracks when the button is being pressed with the mouse:
Work in progress
This PR is still a work-in-progress, and priorities relating to how flushing and controlled components still needs to be worked out. I also intend to add far more tests and ensure that some of the React Flare responders we created in the past, can be modelled on top of this API and redesign.