You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit ef14629
Browse filesBrowse the repository at this point in the historyBrowse files
Add direct-event fast path to EventTarget dispatch (rnIsDirect) (#57448)
Summary:
Pull Request resolved: #57448
Direct events (those that neither bubble nor capture, such as `onLayout`) were still dispatched through the full W3C event-path algorithm when using the EventTarget-based dispatcher: the path was built by walking every ancestor up to the root, and the capture phase then iterated over all of them, even though a direct event only ever fires on its target. This made direct-event dispatch scale with tree depth (O(depth)).
This introduces a "direct" event mode on `Event`:
- A new RN-specific `rnIsDirect` option on `EventInit` (with a matching `rnIsDirect` getter). A direct event has only an `AT_TARGET` phase and an event path containing just the target node.
- Construction validation: an event cannot be both `rnIsDirect` and `bubbles`; the constructor throws a `TypeError` for that combination.
- When `rnIsDirect` is set, the event path is restricted to the target, so dispatch skips the ancestor walk and capture-phase traversal (O(1)).
The renderer opts native direct events into this fast path (behind a runtime gate) by tagging them with `rnIsDirect` at the dispatch site. Bubbling events that merely skip bubbling (e.g. `onPointerEnter`) still have a capture phase and are intentionally not treated as direct.
This is gated and defaults off, so there is no behavior change by default.
Benchmark results (median dispatch latency for the new `onLayout` direct-event benchmarks; all use the same stable-tree setup so depth is directly comparable):
| onLayout dispatch | EventTarget off (baseline) | EventTarget on, direct off | EventTarget on, direct on |
| --- | --- | --- | --- |
| flat (1 handler) | 12.7us | 20.7us | 18.2us |
| nested 10 deep | 12.7us | 33.4us | 18.1us |
| nested 50 deep | 12.7us | 82.2us | 18.2us |
Without the fast path, EventTarget dispatch of a direct event scales with tree depth (20.7us -> 33.4us -> 82.2us). The fast path restores O(1): dispatch cost is flat across depth (~18us) and the nested-50-deep case drops from ~82.2us to ~18.2us (~4.5x faster), approaching the legacy baseline (~12.7us). The EventTarget-off numbers are unchanged by the direct-events flag, confirming the flag is correctly gated.
Changelog: [Internal]
Reviewed By: javache
Differential Revision: D110759162
fbshipit-source-id: f6b2a7ece9bf4a00c6534a69c89c84bc713233d2
'When enabled (together with enableNativeEventTargetEventDispatching), direct events (those that neither bubble nor capture, such as onLayout) are dispatched only to the target node via a fast path that skips construction and traversal of the ancestor event path.',
* When enabled (together with enableNativeEventTargetEventDispatching), direct events (those that neither bubble nor capture, such as onLayout) are dispatched only to the target node via a fast path that skips construction and traversal of the ancestor event path.
* When enabled, ReactNativeElement and ReadOnlyText expose the public EventTarget API (addEventListener, removeEventListener, dispatchEvent). When disabled, those methods are removed from those final classes.
0 commit comments