@@ -18,15 +18,13 @@ import type {
18
18
ReactEventResponderEventType ,
19
19
} from 'shared/ReactTypes' ;
20
20
import type { DOMTopLevelEventType } from 'events/TopLevelEventTypes' ;
21
- import SyntheticEvent from 'events/SyntheticEvent' ;
22
- import { runEventsInBatch } from 'events/EventBatching' ;
23
- import { interactiveUpdates } from 'events/ReactGenericBatching' ;
24
- import { executeDispatch } from 'events/EventPluginUtils' ;
21
+ import { batchedUpdates , interactiveUpdates } from 'events/ReactGenericBatching' ;
25
22
import type { Fiber } from 'react-reconciler/src/ReactFiber' ;
26
-
27
23
import { getClosestInstanceFromNode } from '../client/ReactDOMComponentTree' ;
28
24
29
25
import { enableEventAPI } from 'shared/ReactFeatureFlags' ;
26
+ import { invokeGuardedCallbackAndCatchFirstError } from 'shared/ReactErrorUtils' ;
27
+ import warning from 'shared/warning' ;
30
28
31
29
let listenToResponderEventTypesImpl ;
32
30
@@ -36,6 +34,8 @@ export function setListenToResponderEventTypes(
36
34
listenToResponderEventTypesImpl = _listenToResponderEventTypesImpl ;
37
35
}
38
36
37
+ const PossiblyWeakSet = typeof WeakSet === 'function' ? WeakSet : Set ;
38
+
39
39
const rootEventTypesToEventComponents : Map <
40
40
DOMTopLevelEventType | string ,
41
41
Set < Fiber > ,
@@ -45,12 +45,76 @@ const targetEventTypeCached: Map<
45
45
Set< DOMTopLevelEventType > ,
46
46
> = new Map ( ) ;
47
47
const targetOwnership : Map < EventTarget , Fiber > = new Map ( ) ;
48
+ const eventsWithStopPropagation :
49
+ | WeakSet
50
+ | Set < $Shape < PartialEventObject > > = new PossiblyWeakSet ( ) ;
51
+
52
+ type PartialEventObject = {
53
+ listener : ( $Shape < PartialEventObject > ) => void ,
54
+ target : Element | Document ,
55
+ type : string ,
56
+ } ;
57
+ type EventQueue = {
58
+ bubble : null | Array < $Shape < PartialEventObject >> ,
59
+ capture : null | Array < $Shape < PartialEventObject >> ,
60
+ discrete : boolean ,
61
+ phase : EventQueuePhase ,
62
+ } ;
63
+ type EventQueuePhase = 0 | 1 ;
64
+
65
+ const DURING_EVENT_PHASE = 0 ;
66
+ const AFTER_EVENT_PHASE = 1 ;
48
67
49
- type EventListener = ( event : SyntheticEvent ) => void ;
68
+ function createEventQueue ( phase : EventQueuePhase ) : EventQueue {
69
+ return {
70
+ bubble : null ,
71
+ capture : null ,
72
+ discrete : false ,
73
+ phase,
74
+ } ;
75
+ }
76
+
77
+ function processEvent ( event : $Shape < PartialEventObject > ) : void {
78
+ const type = event . type ;
79
+ const listener = event . listener ;
80
+ invokeGuardedCallbackAndCatchFirstError ( type , listener , undefined , event ) ;
81
+ }
50
82
51
- function copyEventProperties ( eventData , syntheticEvent ) {
52
- for ( let propName in eventData ) {
53
- syntheticEvent [ propName ] = eventData [ propName ] ;
83
+ function processEvents (
84
+ bubble : null | Array < $Shape < PartialEventObject >> ,
85
+ capture : null | Array < $Shape < PartialEventObject >> ,
86
+ ) : void {
87
+ let i , length ;
88
+
89
+ if ( capture !== null ) {
90
+ for ( i = capture . length ; i -- > 0 ; ) {
91
+ const event = capture [ i ] ;
92
+ processEvent ( capture [ i ] ) ;
93
+ if ( eventsWithStopPropagation . has ( event ) ) {
94
+ return ;
95
+ }
96
+ }
97
+ }
98
+ if ( bubble !== null ) {
99
+ for ( i = 0 , length = bubble . length ; i < length ; ++ i ) {
100
+ const event = bubble [ i ] ;
101
+ processEvent ( event ) ;
102
+ if ( eventsWithStopPropagation . has ( event ) ) {
103
+ return ;
104
+ }
105
+ }
106
+ }
107
+ }
108
+
109
+ function processEventQueue ( eventQueue : EventQueue ) : void {
110
+ const { bubble , capture , discrete } = eventQueue ;
111
+
112
+ if ( discrete ) {
113
+ interactiveUpdates ( ( ) => {
114
+ processEvents ( bubble , capture ) ;
115
+ } ) ;
116
+ } else {
117
+ processEvents ( bubble , capture ) ;
54
118
}
55
119
}
56
120
@@ -70,6 +134,7 @@ function DOMEventResponderContext(
70
134
this . _discreteEvents = null ;
71
135
this . _nonDiscreteEvents = null ;
72
136
this . _isBatching = true ;
137
+ this . _eventQueue = createEventQueue ( DURING_EVENT_PHASE ) ;
73
138
}
74
139
75
140
DOMEventResponderContext . prototype . isPassive = function ( ) : boolean {
@@ -81,49 +146,66 @@ DOMEventResponderContext.prototype.isPassiveSupported = function(): boolean {
81
146
} ;
82
147
83
148
DOMEventResponderContext . prototype . dispatchEvent = function (
84
- eventName : string ,
85
- eventListener : EventListener ,
86
- eventTarget : AnyNativeEvent ,
87
- discrete : boolean ,
88
- extraProperties ?: Object ,
149
+ possibleEventObject : Object ,
150
+ {
151
+ capture,
152
+ discrete,
153
+ stopPropagation,
154
+ } : {
155
+ capture ?: boolean ,
156
+ discrete ?: boolean ,
157
+ stopPropagation ?: boolean ,
158
+ } ,
89
159
) : void {
90
- const eventTargetFiber = getClosestInstanceFromNode ( eventTarget ) ;
91
- const syntheticEvent = SyntheticEvent . getPooled (
92
- null ,
93
- eventTargetFiber ,
94
- this . event ,
95
- eventTarget ,
96
- ) ;
97
- if ( extraProperties !== undefined ) {
98
- copyEventProperties ( extraProperties , syntheticEvent ) ;
160
+ const eventQueue = this . _eventQueue ;
161
+ const { listener, target, type} = possibleEventObject ;
162
+
163
+ if ( listener == null || target == null || type == null ) {
164
+ throw new Error (
165
+ 'context.dispatchEvent: "listener", "target" and "type" fields on event object are required.' ,
166
+ ) ;
99
167
}
100
- syntheticEvent . type = eventName ;
101
- syntheticEvent . _dispatchInstances = [ eventTargetFiber ] ;
102
- syntheticEvent . _dispatchListeners = [ eventListener ] ;
103
-
104
- if ( this . _isBatching ) {
105
- let events ;
106
- if ( discrete ) {
107
- events = this . _discreteEvents ;
108
- if ( events === null ) {
109
- events = this . _discreteEvents = [ ] ;
110
- }
111
- } else {
112
- events = this . _nonDiscreteEvents ;
113
- if ( events === null ) {
114
- events = this . _nonDiscreteEvents = [ ] ;
115
- }
168
+ if ( __DEV__ ) {
169
+ possibleEventObject . preventDefault = ( ) => {
170
+ // Update this warning when we have a story around dealing with preventDefault
171
+ warning (
172
+ false ,
173
+ 'preventDefault() is no longer available on event objects created from event responder modules.' ,
174
+ ) ;
175
+ } ;
176
+ possibleEventObject . stopPropagation = ( ) => {
177
+ // Update this warning when we have a story around dealing with stopPropgation
178
+ warning (
179
+ false ,
180
+ 'stopPropagation() is no longer available on event objects created from event responder modules.' ,
181
+ ) ;
182
+ } ;
183
+ }
184
+ const eventObject = ( ( possibleEventObject : any ) : $Shape < PartialEventObject > ) ;
185
+ let events ;
186
+
187
+ if ( capture ) {
188
+ events = eventQueue . capture ;
189
+ if ( events === null ) {
190
+ events = eventQueue . capture = [ ] ;
116
191
}
117
- events . push ( syntheticEvent ) ;
118
192
} else {
119
- if ( discrete ) {
120
- interactiveUpdates ( ( ) => {
121
- executeDispatch ( syntheticEvent , eventListener , eventTargetFiber ) ;
122
- } ) ;
123
- } else {
124
- executeDispatch ( syntheticEvent , eventListener , eventTargetFiber ) ;
193
+ events = eventQueue . bubble ;
194
+ if ( events === null ) {
195
+ events = eventQueue . bubble = [ ] ;
125
196
}
126
197
}
198
+ if ( discrete ) {
199
+ eventQueue . discrete = true ;
200
+ }
201
+ events . push ( eventObject ) ;
202
+
203
+ if ( stopPropagation ) {
204
+ eventsWithStopPropagation . add ( eventObject ) ;
205
+ }
206
+ if ( eventQueue . phase === AFTER_EVENT_PHASE ) {
207
+ batchedUpdates ( processEventQueue , eventQueue ) ;
208
+ }
127
209
} ;
128
210
129
211
DOMEventResponderContext . prototype . isTargetWithinEventComponent = function (
@@ -318,17 +400,9 @@ export function runResponderEventsInBatch(
318
400
) ;
319
401
}
320
402
}
321
- // Run batched events
322
- const discreteEvents = context . _discreteEvents ;
323
- if ( discreteEvents !== null ) {
324
- interactiveUpdates ( ( ) => {
325
- runEventsInBatch ( discreteEvents ) ;
326
- } ) ;
327
- }
328
- const nonDiscreteEvents = context . _nonDiscreteEvents ;
329
- if ( nonDiscreteEvents !== null ) {
330
- runEventsInBatch ( nonDiscreteEvents ) ;
331
- }
332
- context . _isBatching = false ;
403
+ processEventQueue ( context . _eventQueue ) ;
404
+ // In order to capture and process async events from responder modules
405
+ // we create a new event queue.
406
+ context . _eventQueue = createEventQueue ( AFTER_EVENT_PHASE ) ;
333
407
}
334
408
}
0 commit comments