Experimental Event API: Remove "listener" from event objects (#15391)
Dominic Gannaway committed
Apr 12, 2019 at 11:53 UTC
543353a043a9f2ffe4d3e8b20383c05877d0a87e
8 files changed
+139
-125
packages/react-dom/src/events/DOMEventResponderSystem.js
+101
-69
@@ -30,6 +30,7 @@ import type {Fiber} from 'react-reconciler/src/ReactFiber';
30
import warning from 'shared/warning';
31
import {enableEventAPI} from 'shared/ReactFeatureFlags';
32
import {invokeGuardedCallbackAndCatchFirstError} from 'shared/ReactErrorUtils';
33
+import invariant from 'shared/invariant';
34
35
import {getClosestInstanceFromNode} from '../client/ReactDOMComponentTree';
36
@@ -50,7 +51,6 @@ type EventQueue = {
51
};
52
53
type PartialEventObject = {
53
- listener: ($Shape<PartialEventObject>) => void,
54
target: Element | Document,
55
type: string,
56
};
@@ -76,22 +76,31 @@ const targetEventTypeCached: Map<
76
Set<DOMTopLevelEventType>,
77
> = new Map();
78
const ownershipChangeListeners: Set<ReactEventComponentInstance> = new Set();
79
+const PossiblyWeakMap = typeof WeakMap === 'function' ? WeakMap : Map;
80
+const eventListeners:
81
+ | WeakMap
82
+ | Map<
83
+ $Shape<PartialEventObject>,
84
+ ($Shape<PartialEventObject>) => void,
85
+ > = new PossiblyWeakMap();
86
87
let currentTimers = new Map();
88
let currentOwner = null;
82
-let currentInstance: ReactEventComponentInstance;
83
-let currentEventQueue: EventQueue;
89
+let currentInstance: null | ReactEventComponentInstance = null;
90
+let currentEventQueue: null | EventQueue = null;
91
92
const eventResponderContext: ReactResponderContext = {
93
dispatchEvent(
94
possibleEventObject: Object,
95
+ listener: ($Shape<PartialEventObject>) => void,
96
{capture, discrete}: ReactResponderDispatchEventOptions,
97
): void {
90
- const {listener, target, type} = possibleEventObject;
98
+ validateResponderContext();
99
+ const {target, type} = possibleEventObject;
100
92
- if (listener == null || target == null || type == null) {
101
+ if (target == null || type == null) {
102
throw new Error(
94
- 'context.dispatchEvent: "listener", "target" and "type" fields on event object are required.',
103
+ 'context.dispatchEvent: "target" and "type" fields on event object are required.',
104
);
105
}
106
if (__DEV__) {
@@ -115,15 +124,18 @@ const eventResponderContext: ReactResponderContext = {
124
>);
125
const events = getEventsFromEventQueue(capture);
126
if (discrete) {
118
- currentEventQueue.discrete = true;
127
+ ((currentEventQueue: any): EventQueue).discrete = true;
128
}
129
+ eventListeners.set(eventObject, listener);
130
events.push(eventObject);
131
},
132
dispatchStopPropagation(capture?: boolean) {
133
+ validateResponderContext();
134
const events = getEventsFromEventQueue();
135
events.push({stopPropagation: true});
136
},
137
isPositionWithinTouchHitTarget(doc: Document, x: number, y: number): boolean {
138
+ validateResponderContext();
139
// This isn't available in some environments (JSDOM)
140
if (typeof doc.elementFromPoint !== 'function') {
141
return false;
@@ -151,6 +163,7 @@ const eventResponderContext: ReactResponderContext = {
163
return false;
164
},
165
isTargetWithinEventComponent(target: Element | Document): boolean {
166
+ validateResponderContext();
167
if (target != null) {
168
let fiber = getClosestInstanceFromNode(target);
169
while (fiber !== null) {
@@ -182,6 +195,7 @@ const eventResponderContext: ReactResponderContext = {
195
doc: Document,
196
rootEventTypes: Array<ReactEventResponderEventType>,
197
): void {
198
+ validateResponderContext();
199
listenToResponderEventTypesImpl(rootEventTypes, doc);
200
for (let i = 0; i < rootEventTypes.length; i++) {
201
const rootEventType = rootEventTypes[i];
@@ -197,12 +211,15 @@ const eventResponderContext: ReactResponderContext = {
211
rootEventComponentInstances,
212
);
213
}
200
- rootEventComponentInstances.add(currentInstance);
214
+ rootEventComponentInstances.add(
215
+ ((currentInstance: any): ReactEventComponentInstance),
216
+ );
217
}
218
},
219
removeRootEventTypes(
220
rootEventTypes: Array<ReactEventResponderEventType>,
221
): void {
222
+ validateResponderContext();
223
for (let i = 0; i < rootEventTypes.length; i++) {
224
const rootEventType = rootEventTypes[i];
225
const topLevelEventType =
@@ -211,14 +228,18 @@ const eventResponderContext: ReactResponderContext = {
228
topLevelEventType,
229
);
230
if (rootEventComponents !== undefined) {
214
- rootEventComponents.delete(currentInstance);
231
+ rootEventComponents.delete(
232
+ ((currentInstance: any): ReactEventComponentInstance),
233
+ );
234
}
235
}
236
},
237
hasOwnership(): boolean {
238
+ validateResponderContext();
239
return currentOwner === currentInstance;
240
},
241
requestOwnership(): boolean {
242
+ validateResponderContext();
243
if (currentOwner !== null) {
244
return false;
245
}
@@ -227,6 +248,7 @@ const eventResponderContext: ReactResponderContext = {
248
return true;
249
},
250
releaseOwnership(): boolean {
251
+ validateResponderContext();
252
if (currentOwner !== currentInstance) {
253
return false;
254
}
@@ -235,6 +257,7 @@ const eventResponderContext: ReactResponderContext = {
257
return false;
258
},
259
setTimeout(func: () => void, delay): Symbol {
260
+ validateResponderContext();
261
if (currentTimers === null) {
262
currentTimers = new Map();
263
}
@@ -253,7 +276,7 @@ const eventResponderContext: ReactResponderContext = {
276
currentTimers.set(delay, timeout);
277
}
278
timeout.timers.set(timerId, {
256
- instance: currentInstance,
279
+ instance: ((currentInstance: any): ReactEventComponentInstance),
280
func,
281
id: timerId,
282
});
@@ -261,6 +284,7 @@ const eventResponderContext: ReactResponderContext = {
284
return timerId;
285
},
286
clearTimeout(timerId: Symbol): void {
287
+ validateResponderContext();
288
const timeout = activeTimeouts.get(timerId);
289
290
if (timeout !== undefined) {
@@ -279,6 +303,7 @@ const eventResponderContext: ReactResponderContext = {
303
node: Element,
304
props: null | Object,
305
}> {
306
+ validateResponderContext();
307
const eventTargetHostComponents = [];
308
let node = getClosestInstanceFromNode(target);
309
// We traverse up the fiber tree from the target fiber, to the
@@ -326,28 +351,26 @@ const eventResponderContext: ReactResponderContext = {
351
};
352
353
function getEventsFromEventQueue(capture?: boolean): Array<EventObjectTypes> {
354
+ const eventQueue = ((currentEventQueue: any): EventQueue);
355
let events;
356
if (capture) {
331
- events = currentEventQueue.capture;
357
+ events = eventQueue.capture;
358
if (events === null) {
333
- events = currentEventQueue.capture = [];
359
+ events = eventQueue.capture = [];
360
}
361
} else {
336
- events = currentEventQueue.bubble;
362
+ events = eventQueue.bubble;
363
if (events === null) {
338
- events = currentEventQueue.bubble = [];
364
+ events = eventQueue.bubble = [];
365
}
366
}
367
return events;
368
}
369
370
function processTimers(timers: Map<Symbol, ResponderTimer>): void {
345
- const previousEventQueue = currentEventQueue;
346
- const previousInstance = currentInstance;
371
+ const timersArr = Array.from(timers.values());
372
currentEventQueue = createEventQueue();
348
-
373
try {
350
- const timersArr = Array.from(timers.values());
374
for (let i = 0; i < timersArr.length; i++) {
375
const {instance, func, id} = timersArr[i];
376
currentInstance = instance;
@@ -359,9 +382,9 @@ function processTimers(timers: Map<Symbol, ResponderTimer>): void {
382
}
383
batchedUpdates(processEventQueue, currentEventQueue);
384
} finally {
362
- currentInstance = previousInstance;
363
- currentEventQueue = previousEventQueue;
385
currentTimers = null;
386
+ currentInstance = null;
387
+ currentEventQueue = null;
388
}
389
}
390
@@ -404,7 +427,9 @@ function createEventQueue(): EventQueue {
427
428
function processEvent(event: $Shape<PartialEventObject>): void {
429
const type = event.type;
407
- const listener = event.listener;
430
+ const listener = ((eventListeners.get(event): any): (
431
+ $Shape<PartialEventObject>,
432
+ ) => void);
433
invokeGuardedCallbackAndCatchFirstError(type, listener, undefined, event);
434
}
435
@@ -435,7 +460,7 @@ function processEvents(
460
}
461
462
export function processEventQueue(): void {
438
- const {bubble, capture, discrete} = currentEventQueue;
463
+ const {bubble, capture, discrete} = ((currentEventQueue: any): EventQueue);
464
465
if (discrete) {
466
interactiveUpdates(() => {
@@ -478,13 +503,8 @@ function handleTopLevelType(
503
return;
504
}
505
}
481
- const previousInstance = currentInstance;
506
currentInstance = eventComponentInstance;
483
- try {
484
- responder.onEvent(responderEvent, eventResponderContext, props, state);
485
- } finally {
486
- currentInstance = previousInstance;
487
- }
507
+ responder.onEvent(responderEvent, eventResponderContext, props, state);
508
}
509
510
export function runResponderEventsInBatch(
@@ -502,54 +522,60 @@ export function runResponderEventsInBatch(
522
((nativeEventTarget: any): Element | Document),
523
eventSystemFlags,
524
);
505
- let node = targetFiber;
506
- // Traverse up the fiber tree till we find event component fibers.
507
- while (node !== null) {
508
- if (node.tag === EventComponent) {
509
- const eventComponentInstance = node.stateNode;
510
- handleTopLevelType(
511
- topLevelType,
512
- responderEvent,
513
- eventComponentInstance,
514
- false,
515
- );
525
+
526
+ try {
527
+ let node = targetFiber;
528
+ // Traverse up the fiber tree till we find event component fibers.
529
+ while (node !== null) {
530
+ if (node.tag === EventComponent) {
531
+ const eventComponentInstance = node.stateNode;
532
+ handleTopLevelType(
533
+ topLevelType,
534
+ responderEvent,
535
+ eventComponentInstance,
536
+ false,
537
+ );
538
+ }
539
+ node = node.return;
540
}
517
- node = node.return;
518
- }
519
- // Handle root level events
520
- const rootEventInstances = rootEventTypesToEventComponentInstances.get(
521
- topLevelType,
522
- );
523
- if (rootEventInstances !== undefined) {
524
- const rootEventComponentInstances = Array.from(rootEventInstances);
525
-
526
- for (let i = 0; i < rootEventComponentInstances.length; i++) {
527
- const rootEventComponentInstance = rootEventComponentInstances[i];
528
- handleTopLevelType(
529
- topLevelType,
530
- responderEvent,
531
- rootEventComponentInstance,
532
- true,
533
- );
541
+ // Handle root level events
542
+ const rootEventInstances = rootEventTypesToEventComponentInstances.get(
543
+ topLevelType,
544
+ );
545
+ if (rootEventInstances !== undefined) {
546
+ const rootEventComponentInstances = Array.from(rootEventInstances);
547
+
548
+ for (let i = 0; i < rootEventComponentInstances.length; i++) {
549
+ const rootEventComponentInstance = rootEventComponentInstances[i];
550
+ handleTopLevelType(
551
+ topLevelType,
552
+ responderEvent,
553
+ rootEventComponentInstance,
554
+ true,
555
+ );
556
+ }
557
}
558
+ processEventQueue();
559
+ } finally {
560
+ currentTimers = null;
561
+ currentInstance = null;
562
+ currentEventQueue = null;
563
}
536
- processEventQueue();
537
- currentTimers = null;
564
}
565
}
566
567
function triggerOwnershipListeners(): void {
568
const listeningInstances = Array.from(ownershipChangeListeners);
569
const previousInstance = currentInstance;
544
- for (let i = 0; i < listeningInstances.length; i++) {
545
- const instance = listeningInstances[i];
546
- const {props, responder, state} = instance;
547
- currentInstance = instance;
548
- try {
570
+ try {
571
+ for (let i = 0; i < listeningInstances.length; i++) {
572
+ const instance = listeningInstances[i];
573
+ const {props, responder, state} = instance;
574
+ currentInstance = instance;
575
responder.onOwnershipChange(eventResponderContext, props, state);
550
- } finally {
551
- currentInstance = previousInstance;
576
}
577
+ } finally {
578
+ currentInstance = previousInstance;
579
}
580
}
581
@@ -569,15 +595,13 @@ export function unmountEventResponder(
595
const onUnmount = responder.onUnmount;
596
if (onUnmount !== undefined) {
597
let {props, state} = eventComponentInstance;
572
- const previousEventQueue = currentEventQueue;
573
- const previousInstance = currentInstance;
598
currentEventQueue = createEventQueue();
599
currentInstance = eventComponentInstance;
600
try {
601
onUnmount(eventResponderContext, props, state);
602
} finally {
579
- currentEventQueue = previousEventQueue;
580
- currentInstance = previousInstance;
603
+ currentEventQueue = null;
604
+ currentInstance = null;
605
currentTimers = null;
606
}
607
}
@@ -589,3 +613,11 @@ export function unmountEventResponder(
613
ownershipChangeListeners.delete(eventComponentInstance);
614
}
615
}
616
+
617
+function validateResponderContext(): void {
618
+ invariant(
619
+ currentEventQueue && currentInstance,
620
+ 'An event responder context was used outside of an event cycle. ' +
621
+ 'Use context.setTimeout() to use asynchronous responder context outside of event cycle .',
622
+ );
623
+}
packages/react-dom/src/events/__tests__/DOMEventResponderSystem-test.internal.js
+12
-8
@@ -255,11 +255,12 @@ describe('DOMEventResponderSystem', () => {
255
(event, context, props) => {
256
if (props.onMagicClick) {
257
const syntheticEvent = {
258
- listener: props.onMagicClick,
258
target: event.target,
259
type: 'magicclick',
260
};
262
- context.dispatchEvent(syntheticEvent, {discrete: true});
261
+ context.dispatchEvent(syntheticEvent, props.onMagicClick, {
262
+ discrete: true,
263
+ });
264
}
265
},
266
);
@@ -292,29 +293,32 @@ describe('DOMEventResponderSystem', () => {
293
undefined,
294
(event, context, props) => {
295
const pressEvent = {
295
- listener: props.onPress,
296
target: event.target,
297
type: 'press',
298
};
299
- context.dispatchEvent(pressEvent, {discrete: true});
299
+ context.dispatchEvent(pressEvent, props.onPress, {discrete: true});
300
301
context.setTimeout(() => {
302
if (props.onLongPress) {
303
const longPressEvent = {
304
- listener: props.onLongPress,
304
target: event.target,
305
type: 'longpress',
306
};
308
- context.dispatchEvent(longPressEvent, {discrete: true});
307
+ context.dispatchEvent(longPressEvent, props.onLongPress, {
308
+ discrete: true,
309
+ });
310
}
311
312
if (props.onLongPressChange) {
313
const longPressChangeEvent = {
313
- listener: props.onLongPressChange,
314
target: event.target,
315
type: 'longpresschange',
316
};
317
- context.dispatchEvent(longPressChangeEvent, {discrete: true});
317
+ context.dispatchEvent(
318
+ longPressChangeEvent,
319
+ props.onLongPressChange,
320
+ {discrete: true},
321
+ );
322
}
323
}, 500);
324
},
packages/react-events/src/Drag.js
+2
-5
@@ -43,7 +43,6 @@ type EventData = {
43
type DragEventType = 'dragstart' | 'dragend' | 'dragchange' | 'dragmove';
44
45
type DragEvent = {|
46
- listener: DragEvent => void,
46
target: Element | Document,
47
type: DragEventType,
48
diffX?: number,
@@ -53,11 +52,9 @@ type DragEvent = {|
52
function createDragEvent(
53
type: DragEventType,
54
target: Element | Document,
56
- listener: DragEvent => void,
55
eventData?: EventData,
56
): DragEvent {
57
return {
60
- listener,
58
target,
59
type,
60
...eventData,
@@ -73,8 +70,8 @@ function dispatchDragEvent(
70
eventData?: EventData,
71
): void {
72
const target = ((state.dragTarget: any): Element | Document);
76
- const syntheticEvent = createDragEvent(name, target, listener, eventData);
77
- context.dispatchEvent(syntheticEvent, {discrete});
73
+ const syntheticEvent = createDragEvent(name, target, eventData);
74
+ context.dispatchEvent(syntheticEvent, listener, {discrete});
75
}
76
77
const DragResponder = {
packages/react-events/src/Focus.js
+8
-11
@@ -27,7 +27,6 @@ type FocusState = {
27
type FocusEventType = 'focus' | 'blur' | 'focuschange';
28
29
type FocusEvent = {|
30
- listener: FocusEvent => void,
30
target: Element | Document,
31
type: FocusEventType,
32
|};
@@ -40,10 +39,8 @@ const targetEventTypes = [
39
function createFocusEvent(
40
type: FocusEventType,
41
target: Element | Document,
43
- listener: FocusEvent => void,
42
): FocusEvent {
43
return {
46
- listener,
44
target,
45
type,
46
};
@@ -59,15 +56,15 @@ function dispatchFocusInEvents(
56
return;
57
}
58
if (props.onFocus) {
62
- const syntheticEvent = createFocusEvent('focus', target, props.onFocus);
63
- context.dispatchEvent(syntheticEvent, {discrete: true});
59
+ const syntheticEvent = createFocusEvent('focus', target);
60
+ context.dispatchEvent(syntheticEvent, props.onFocus, {discrete: true});
61
}
62
if (props.onFocusChange) {
63
const listener = () => {
64
props.onFocusChange(true);
65
};
69
- const syntheticEvent = createFocusEvent('focuschange', target, listener);
70
- context.dispatchEvent(syntheticEvent, {discrete: true});
66
+ const syntheticEvent = createFocusEvent('focuschange', target);
67
+ context.dispatchEvent(syntheticEvent, listener, {discrete: true});
68
}
69
}
70
@@ -81,15 +78,15 @@ function dispatchFocusOutEvents(
78
return;
79
}
80
if (props.onBlur) {
84
- const syntheticEvent = createFocusEvent('blur', target, props.onBlur);
85
- context.dispatchEvent(syntheticEvent, {discrete: true});
81
+ const syntheticEvent = createFocusEvent('blur', target);
82
+ context.dispatchEvent(syntheticEvent, props.onBlur, {discrete: true});
83
}
84
if (props.onFocusChange) {
85
const listener = () => {
86
props.onFocusChange(false);
87
};
91
- const syntheticEvent = createFocusEvent('focuschange', target, listener);
92
- context.dispatchEvent(syntheticEvent, {discrete: true});
88
+ const syntheticEvent = createFocusEvent('focuschange', target);
89
+ context.dispatchEvent(syntheticEvent, listener, {discrete: true});
90
}
91
}
92
packages/react-events/src/Hover.js
+11
-23
@@ -36,7 +36,6 @@ type HoverState = {
36
type HoverEventType = 'hoverstart' | 'hoverend' | 'hoverchange' | 'hovermove';
37
38
type HoverEvent = {|
39
- listener: HoverEvent => void,
39
target: Element | Document,
40
type: HoverEventType,
41
|};
@@ -59,10 +58,8 @@ if (typeof window !== 'undefined' && window.PointerEvent === undefined) {
58
function createHoverEvent(
59
type: HoverEventType,
60
target: Element | Document,
62
- listener: HoverEvent => void,
61
): HoverEvent {
62
return {
65
- listener,
63
target,
64
type,
65
};
@@ -78,12 +75,8 @@ function dispatchHoverChangeEvent(
75
const listener = () => {
76
props.onHoverChange(bool);
77
};
81
- const syntheticEvent = createHoverEvent(
82
- 'hoverchange',
83
- event.target,
84
- listener,
85
- );
86
- context.dispatchEvent(syntheticEvent, {discrete: true});
78
+ const syntheticEvent = createHoverEvent('hoverchange', event.target);
79
+ context.dispatchEvent(syntheticEvent, listener, {discrete: true});
80
}
81
82
function dispatchHoverStartEvents(
@@ -108,12 +101,10 @@ function dispatchHoverStartEvents(
101
state.isActiveHovered = true;
102
103
if (props.onHoverStart) {
111
- const syntheticEvent = createHoverEvent(
112
- 'hoverstart',
113
- target,
114
- props.onHoverStart,
115
- );
116
- context.dispatchEvent(syntheticEvent, {discrete: true});
104
+ const syntheticEvent = createHoverEvent('hoverstart', target);
105
+ context.dispatchEvent(syntheticEvent, props.onHoverStart, {
106
+ discrete: true,
107
+ });
108
}
109
if (props.onHoverChange) {
110
dispatchHoverChangeEvent(event, context, props, state);
@@ -159,12 +150,8 @@ function dispatchHoverEndEvents(
150
state.isActiveHovered = false;
151
152
if (props.onHoverEnd) {
162
- const syntheticEvent = createHoverEvent(
163
- 'hoverend',
164
- target,
165
- props.onHoverEnd,
166
- );
167
- context.dispatchEvent(syntheticEvent, {discrete: true});
153
+ const syntheticEvent = createHoverEvent('hoverend', target);
154
+ context.dispatchEvent(syntheticEvent, props.onHoverEnd, {discrete: true});
155
}
156
if (props.onHoverChange) {
157
dispatchHoverChangeEvent(event, context, props, state);
@@ -292,9 +279,10 @@ const HoverResponder = {
279
const syntheticEvent = createHoverEvent(
280
'hovermove',
281
event.target,
295
- props.onHoverMove,
282
);
297
- context.dispatchEvent(syntheticEvent, {discrete: false});
283
+ context.dispatchEvent(syntheticEvent, props.onHoverMove, {
284
+ discrete: false,
285
+ });
286
}
287
}
288
}
packages/react-events/src/Press.js
+2
-4
@@ -71,7 +71,6 @@ type PressEventType =
71
| 'longpresschange';
72
73
type PressEvent = {|
74
- listener: PressEvent => void,
74
target: Element | Document,
75
type: PressEventType,
76
pointerType: PointerType,
@@ -115,11 +114,9 @@ if (typeof window !== 'undefined' && window.PointerEvent === undefined) {
114
function createPressEvent(
115
type: PressEventType,
116
target: Element | Document,
118
- listener: PressEvent => void,
117
pointerType: PointerType,
118
): PressEvent {
119
return {
122
- listener,
120
target,
121
type,
122
pointerType,
@@ -135,9 +132,10 @@ function dispatchEvent(
132
): void {
133
const target = ((state.pressTarget: any): Element | Document);
134
const pointerType = state.pointerType;
138
- const syntheticEvent = createPressEvent(name, target, listener, pointerType);
135
+ const syntheticEvent = createPressEvent(name, target, pointerType);
136
context.dispatchEvent(
137
syntheticEvent,
138
+ listener,
139
options || {
140
discrete: true,
141
},
packages/react-events/src/Swipe.js
+2
-5
@@ -33,7 +33,6 @@ type EventData = {
33
type SwipeEventType = 'swipeleft' | 'swiperight' | 'swipeend' | 'swipemove';
34
35
type SwipeEvent = {|
36
- listener: SwipeEvent => void,
36
target: Element | Document,
37
type: SwipeEventType,
38
diffX?: number,
@@ -43,11 +42,9 @@ type SwipeEvent = {|
42
function createSwipeEvent(
43
type: SwipeEventType,
44
target: Element | Document,
46
- listener: SwipeEvent => void,
45
eventData?: EventData,
46
): SwipeEvent {
47
return {
50
- listener,
48
target,
49
type,
50
...eventData,
@@ -63,8 +60,8 @@ function dispatchSwipeEvent(
60
eventData?: EventData,
61
) {
62
const target = ((state.swipeTarget: any): Element | Document);
66
- const syntheticEvent = createSwipeEvent(name, target, listener, eventData);
67
- context.dispatchEvent(syntheticEvent, {discrete});
63
+ const syntheticEvent = createSwipeEvent(name, target, eventData);
64
+ context.dispatchEvent(syntheticEvent, listener, {discrete});
65
}
66
67
type SwipeState = {
packages/shared/ReactTypes.js
+1
@@ -145,6 +145,7 @@ export type ReactResponderDispatchEventOptions = {
145
export type ReactResponderContext = {
146
dispatchEvent: (
147
eventObject: Object,
148
+ listener: (Object) => void,
149
otpions: ReactResponderDispatchEventOptions,
150
) => void,
151
dispatchStopPropagation: (passive?: boolean) => void,