-
Notifications
You must be signed in to change notification settings - Fork 47k
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
Do not bind topLevelType to dispatch #13618
Conversation
@@ -16,7 +16,7 @@ import type {TopLevelType} from './TopLevelEventTypes'; | |||
|
|||
export type EventTypes = {[key: string]: DispatchConfig}; | |||
|
|||
export type AnyNativeEvent = Event | KeyboardEvent | MouseEvent | Touch; | |||
export type AnyNativeEvent = Event | KeyboardEvent | MouseEvent | TouchEvent; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should probably go straight to master. As far as I can tell, this is a bug, but I don't know if Touch is defined somewhere else or is actually correctly referencing the Touch
DOM constructor
export function interactiveUpdates(fn, a, b) { | ||
return _interactiveUpdatesImpl(fn, a, b); | ||
export function interactiveUpdates(fn, a) { | ||
return _interactiveUpdatesImpl(fn, a); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This possibly affects other renders 😨
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have you checked whether it does? ReactGenericBatching
is only accessible in this repository.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should not affect other renders. ReactGenericBatching.interactiveUpdates is only used inside of ReactDOMEventListener.
a88ce01
to
b319235
Compare
Details of bundled changes.Comparing: f6fb03e...16ad8d6 schedule
Generated by 🚫 dangerJS |
b319235
to
ab0b248
Compare
This by itself seems fine to me and is a bit simpler. What's the concern? |
@gaearon No concerns on my end. I just didn't know if I missed something and wasn't going to push the change too hard. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks fine for me as well 🙂
Instead of focusing on your follow ups we should probably start to lay out the „new“ event system Dan mentioned in #13613 (comment) - Ideally all of those issues will be irrelevant by then.
@@ -42,7 +42,9 @@ const [ | |||
runEventsInBatch, | |||
] = ReactDOM.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.Events; | |||
|
|||
function Event(suffix) {} | |||
function Event(type) { | |||
this.type = type; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I‘m a bit confused about this - where do we use this event constructor?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RestTestUtils.SimulateNative:
react/packages/react-dom/src/test-utils/ReactTestUtils.js
Lines 479 to 498 in f6fb03e
function makeNativeSimulator(eventType, topLevelType) { | |
return function(domComponentOrNode, nativeEventData) { | |
const fakeNativeEvent = new Event(eventType); | |
Object.assign(fakeNativeEvent, nativeEventData); | |
if (ReactTestUtils.isDOMComponent(domComponentOrNode)) { | |
simulateNativeEventOnDOMComponent( | |
topLevelType, | |
domComponentOrNode, | |
fakeNativeEvent, | |
); | |
} else if (domComponentOrNode.tagName) { | |
// Will allow on actual dom nodes. | |
simulateNativeEventOnNode( | |
topLevelType, | |
domComponentOrNode, | |
fakeNativeEvent, | |
); | |
} | |
}; | |
} |
@@ -57,6 +57,8 @@ function getTopLevelCallbackBookKeeping( | |||
targetInst: Fiber | null, | |||
ancestors: Array<Fiber>, | |||
} { | |||
const topLevelType = unsafeCastStringToDOMTopLevelType(nativeEvent.type); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the guideline for when it's "safe" to call it? How do we know it's okay here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's safe here because we know the nativeEvent type is always a DOMTopLevelType.
This is otherwise only used to cast the top level types themselves when they are created, like:
react/packages/react-dom/src/events/DOMTopLevelEventTypes.js
Lines 25 to 31 in f6fb03e
export const TOP_ABORT = unsafeCastStringToDOMTopLevelType('abort'); | |
export const TOP_ANIMATION_END = unsafeCastStringToDOMTopLevelType( | |
getVendorPrefixedEventName('animationend'), | |
); | |
export const TOP_ANIMATION_ITERATION = unsafeCastStringToDOMTopLevelType( | |
getVendorPrefixedEventName('animationiteration'), | |
); |
I wonder if there is an existing concept of a DOM event's type string. We don't really have top level types in anymore: just the browser standard event names.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should have a comment explaining why then.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Commented! Part of me wonders if these could be annotated using the actual event type declarations here:
If that were the case, we could remove the concept of a top level type and just make it an DOM event type
@@ -149,7 +152,7 @@ export function trapBubbledEvent( | |||
element, | |||
getRawEventName(topLevelType), | |||
// Check if interactive and wrap in interactiveUpdates |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment should move above btw.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in 60de292
A previous change made it such that all top level event types correspond to their associated native event string values. This commit eliminates the .bind attached to dispatch and fixes a related flow type.
60de292
to
2581987
Compare
@gaearon I think this is good to go, but would you like me to hold off on a merge during the sync? |
It's too late for sync today (spent more time than I thought) so I'll finish it tomorrow. And I plan to cut an open source patch on Monday. So let's wait until then. |
(For context: since @acdlite merged the Scheduler change we'd need to sync on Monday anyway. So this doesn't have to delay now.) |
Haha. Thanks. I wonder if anyone thought I was going rogue 😅 |
* Do not bind topLevelType to dispatch A previous change made it such that all top level event types correspond to their associated native event string values. This commit eliminates the .bind attached to dispatch and fixes a related flow type. * Add note about why casting event.type to a topLevelType is safe * Move interactiveUpdates comment to point of assignment
* Revert "Do not bind topLevelType to dispatch (facebook#13618)" This reverts commit 0c9c591.
* Do not bind topLevelType to dispatch A previous change made it such that all top level event types correspond to their associated native event string values. This commit eliminates the .bind attached to dispatch and fixes a related flow type. * Add note about why casting event.type to a topLevelType is safe * Move interactiveUpdates comment to point of assignment
* Revert "Do not bind topLevelType to dispatch (facebook#13618)" This reverts commit 0c9c591.
This PR is exploratory, but it's been on my mind a long time and I wanted to get others' thoughts.
A previous change (#12629) made it such that all top level event types correspond to their associated native event string values. That means we don't need to bind that event name into
dispatchEvent
, letting all event listener code use either the standard dispatch function, or the variant for interactive updates.Why
My primary motivation with this PR isn't to necessarily merge this, but to talk about a few event system things.
Why drop bind()
Dropping this
.bind()
is attractive to me because I believe we could eventually remove the logic inReactDOMBrowserEventEmitter
to track what events have already attached:react/packages/react-dom/src/events/ReactBrowserEventEmitter.js
Lines 95 to 103 in f6fb03e
react/packages/react-dom/src/events/ReactBrowserEventEmitter.js
Lines 130 to 135 in f6fb03e
When working on the scroll-jank PR (#9333), tracking needs to happen per-element for touch, wheel, and scroll events. That means saving state on each DOM node, and doing a lot of property access.
Could it be faster?
I'm curious about the performance characteristics of naively attaching the same event. It could avoid a lot of DOM storage and property access (event listener attachment tracking) if we start to attach listeners locally, since
addEventListener
is idempotent:Challenges
We can't remove event listener tracking because SelectEventPlugin depends on this tracking to see if it should fire:
react/packages/react-dom/src/events/SelectEventPlugin.js
Lines 173 to 177 in f6fb03e
But we might be able to come up with another way to determine when SelectEventPlugin should fire. Maybe, instead of enumerating all plugins at dispatch time, they could be eagerly applied on mount. Then we also wouldn't have to run through all event plugins on every dispatch because the event plugins would have the context to know that their behavior was specifically applied.
Still, it's an interesting series of changes to me, and I wanted to get the thoughts of others.