Address feedback for accumulateTwoPhaseListeners (#18289)
Dominic Gannaway committed
Mar 12, 2020 at 21:48 UTC
d3ec42020d220777bf589bec36c9cd8300ec9742
13 files changed
+315
-147
packages/legacy-events/EventPropagators.js
+21
-75
@@ -3,30 +3,19 @@
3
*
4
* This source code is licensed under the MIT license found in the
5
* LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow
8
*/
9
8
-import {
9
- getParentInstance,
10
- traverseTwoPhase,
11
- traverseEnterLeave,
12
-} from 'shared/ReactTreeTraversal';
10
+import type {Fiber} from 'react-reconciler/src/ReactFiber';
11
+import type {ReactSyntheticEvent} from 'legacy-events/ReactSyntheticEventType';
12
13
import getListener from 'legacy-events/getListener';
14
+
15
+import {traverseEnterLeave} from 'shared/ReactTreeTraversal';
16
import accumulateInto from './accumulateInto';
17
import forEachAccumulated from './forEachAccumulated';
18
18
-type PropagationPhases = 'bubbled' | 'captured';
19
-
20
-/**
21
- * Some event types have a notion of different registration names for different
22
- * "phases" of propagation. This finds listeners by a given phase.
23
- */
24
-function listenerAtPhase(inst, event, propagationPhase: PropagationPhases) {
25
- const registrationName =
26
- event.dispatchConfig.phasedRegistrationNames[propagationPhase];
27
- return getListener(inst, registrationName);
28
-}
29
-
19
/**
20
* A small set of propagation patterns, each of which will accept a small amount
21
* of information, and generate a set of "dispatch ready event objects" - which
@@ -37,58 +26,16 @@ function listenerAtPhase(inst, event, propagationPhase: PropagationPhases) {
26
* single one.
27
*/
28
40
-/**
41
- * Tags a `SyntheticEvent` with dispatched listeners. Creating this function
42
- * here, allows us to not have to bind or create functions for each event.
43
- * Mutating the event's members allows us to not have to create a wrapping
44
- * "dispatch" object that pairs the event with the listener.
45
- */
46
-function accumulateDirectionalDispatches(inst, phase, event) {
47
- if (__DEV__) {
48
- if (!inst) {
49
- console.error('Dispatching inst must not be null');
50
- }
51
- }
52
- const listener = listenerAtPhase(inst, event, phase);
53
- if (listener) {
54
- event._dispatchListeners = accumulateInto(
55
- event._dispatchListeners,
56
- listener,
57
- );
58
- event._dispatchInstances = accumulateInto(event._dispatchInstances, inst);
59
- }
60
-}
61
-
62
-/**
63
- * Collect dispatches (must be entirely collected before dispatching - see unit
64
- * tests). Lazily allocate the array to conserve memory. We must loop through
65
- * each event and perform the traversal for each one. We cannot perform a
66
- * single traversal for the entire collection of events because each event may
67
- * have a different target.
68
- */
69
-export function accumulateTwoPhaseDispatchesSingle(event) {
70
- if (event && event.dispatchConfig.phasedRegistrationNames) {
71
- traverseTwoPhase(event._targetInst, accumulateDirectionalDispatches, event);
72
- }
73
-}
74
-
75
-/**
76
- * Same as `accumulateTwoPhaseDispatchesSingle`, but skips over the targetID.
77
- */
78
-function accumulateTwoPhaseDispatchesSingleSkipTarget(event) {
79
- if (event && event.dispatchConfig.phasedRegistrationNames) {
80
- const targetInst = event._targetInst;
81
- const parentInst = targetInst ? getParentInstance(targetInst) : null;
82
- traverseTwoPhase(parentInst, accumulateDirectionalDispatches, event);
83
- }
84
-}
85
-
29
/**
30
* Accumulates without regard to direction, does not look for phased
31
* registration names. Same as `accumulateDirectDispatchesSingle` but without
32
* requiring that the `dispatchMarker` be the same as the dispatched ID.
33
*/
91
-function accumulateDispatches(inst, ignoredDirection, event) {
34
+function accumulateDispatches(
35
+ inst: Fiber,
36
+ ignoredDirection: ?boolean,
37
+ event: ReactSyntheticEvent,
38
+): void {
39
if (inst && event && event.dispatchConfig.registrationName) {
40
const registrationName = event.dispatchConfig.registrationName;
41
const listener = getListener(inst, registrationName);
@@ -107,24 +54,23 @@ function accumulateDispatches(inst, ignoredDirection, event) {
54
* `dispatchMarker`.
55
* @param {SyntheticEvent} event
56
*/
110
-function accumulateDirectDispatchesSingle(event) {
57
+function accumulateDirectDispatchesSingle(event: ReactSyntheticEvent) {
58
if (event && event.dispatchConfig.registrationName) {
59
accumulateDispatches(event._targetInst, null, event);
60
}
61
}
62
116
-export function accumulateTwoPhaseDispatches(events) {
117
- forEachAccumulated(events, accumulateTwoPhaseDispatchesSingle);
118
-}
119
-
120
-export function accumulateTwoPhaseDispatchesSkipTarget(events) {
121
- forEachAccumulated(events, accumulateTwoPhaseDispatchesSingleSkipTarget);
122
-}
123
-
124
-export function accumulateEnterLeaveDispatches(leave, enter, from, to) {
63
+export function accumulateEnterLeaveDispatches(
64
+ leave: ReactSyntheticEvent,
65
+ enter: ReactSyntheticEvent,
66
+ from: Fiber,
67
+ to: Fiber,
68
+) {
69
traverseEnterLeave(from, to, accumulateDispatches, leave, enter);
70
}
71
128
-export function accumulateDirectDispatches(events) {
72
+export function accumulateDirectDispatches(
73
+ events: ?(Array<ReactSyntheticEvent> | ReactSyntheticEvent),
74
+) {
75
forEachAccumulated(events, accumulateDirectDispatchesSingle);
76
}
packages/legacy-events/ReactSyntheticEventType.js
+3
-3
@@ -31,8 +31,8 @@ export type ReactSyntheticEvent = {|
31
nativeEventTarget: EventTarget,
32
) => ReactSyntheticEvent,
33
isPersistent: () => boolean,
34
- _dispatchInstances: null | Array<Fiber>,
35
- _dispatchListeners: null | Array<Function>,
36
- _targetInst: null | Fiber,
34
+ _dispatchInstances: null | Array<Fiber> | Fiber,
35
+ _dispatchListeners: null | Array<Function> | Function,
36
+ _targetInst: Fiber,
37
type: string,
38
|};
packages/legacy-events/ResponderEventPlugin.js
+58
-6
@@ -5,7 +5,12 @@
5
* LICENSE file in the root directory of this source tree.
6
*/
7
8
-import {getLowestCommonAncestor, isAncestor} from 'shared/ReactTreeTraversal';
8
+import {
9
+ getLowestCommonAncestor,
10
+ isAncestor,
11
+ getParentInstance,
12
+ traverseTwoPhase,
13
+} from 'shared/ReactTreeTraversal';
14
15
import {
16
executeDirectDispatch,
@@ -13,11 +18,7 @@ import {
18
executeDispatchesInOrderStopAtTrue,
19
getInstanceFromNode,
20
} from './EventPluginUtils';
16
-import {
17
- accumulateDirectDispatches,
18
- accumulateTwoPhaseDispatches,
19
- accumulateTwoPhaseDispatchesSkipTarget,
20
-} from './EventPropagators';
21
+import {accumulateDirectDispatches} from './EventPropagators';
22
import ResponderSyntheticEvent from './ResponderSyntheticEvent';
23
import ResponderTouchHistoryStore from './ResponderTouchHistoryStore';
24
import accumulate from './accumulate';
@@ -32,6 +33,9 @@ import {
33
moveDependencies,
34
endDependencies,
35
} from './ResponderTopLevelEventTypes';
36
+import getListener from './getListener';
37
+import accumulateInto from './accumulateInto';
38
+import forEachAccumulated from './forEachAccumulated';
39
40
/**
41
* Instance of element that should respond to touch/move types of interactions,
@@ -151,6 +155,54 @@ const eventTypes = {
155
},
156
};
157
158
+// Start of inline: the below functions were inlined from
159
+// EventPropagator.js, as they deviated from ReactDOM's newer
160
+// implementations.
161
+function listenerAtPhase(inst, event, propagationPhase: PropagationPhases) {
162
+ const registrationName =
163
+ event.dispatchConfig.phasedRegistrationNames[propagationPhase];
164
+ return getListener(inst, registrationName);
165
+}
166
+
167
+function accumulateDirectionalDispatches(inst, phase, event) {
168
+ if (__DEV__) {
169
+ if (!inst) {
170
+ console.error('Dispatching inst must not be null');
171
+ }
172
+ }
173
+ const listener = listenerAtPhase(inst, event, phase);
174
+ if (listener) {
175
+ event._dispatchListeners = accumulateInto(
176
+ event._dispatchListeners,
177
+ listener,
178
+ );
179
+ event._dispatchInstances = accumulateInto(event._dispatchInstances, inst);
180
+ }
181
+}
182
+
183
+function accumulateTwoPhaseDispatchesSingleSkipTarget(event) {
184
+ if (event && event.dispatchConfig.phasedRegistrationNames) {
185
+ const targetInst = event._targetInst;
186
+ const parentInst = targetInst ? getParentInstance(targetInst) : null;
187
+ traverseTwoPhase(parentInst, accumulateDirectionalDispatches, event);
188
+ }
189
+}
190
+
191
+function accumulateTwoPhaseDispatchesSkipTarget(events) {
192
+ forEachAccumulated(events, accumulateTwoPhaseDispatchesSingleSkipTarget);
193
+}
194
+
195
+function accumulateTwoPhaseDispatchesSingle(event) {
196
+ if (event && event.dispatchConfig.phasedRegistrationNames) {
197
+ traverseTwoPhase(event._targetInst, accumulateDirectionalDispatches, event);
198
+ }
199
+}
200
+
201
+function accumulateTwoPhaseDispatches(events) {
202
+ forEachAccumulated(events, accumulateTwoPhaseDispatchesSingle);
203
+}
204
+// End of inline
205
+
206
/**
207
*
208
* Responder System:
packages/react-dom/src/client/ReactDOM.js
-6
@@ -50,10 +50,6 @@ import {
50
eventNameDispatchConfigs,
51
injectEventPluginsByName,
52
} from 'legacy-events/EventPluginRegistry';
53
-import {
54
- accumulateTwoPhaseDispatches,
55
- accumulateDirectDispatches,
56
-} from 'legacy-events/EventPropagators';
53
import ReactVersion from 'shared/ReactVersion';
54
import invariant from 'shared/invariant';
55
import {warnUnstableRenderSubtreeIntoContainer} from 'shared/ReactFeatureFlags';
@@ -184,8 +180,6 @@ const Internals = {
180
getFiberCurrentPropsFromNode,
181
injectEventPluginsByName,
182
eventNameDispatchConfigs,
187
- accumulateTwoPhaseDispatches,
188
- accumulateDirectDispatches,
183
enqueueStateRestore,
184
restoreStateIfNeeded,
185
dispatchEvent,
packages/react-dom/src/events/BeforeInputEventPlugin.js
+1
-1
@@ -28,7 +28,7 @@ import {
28
} from './FallbackCompositionState';
29
import SyntheticCompositionEvent from './SyntheticCompositionEvent';
30
import SyntheticInputEvent from './SyntheticInputEvent';
31
-import {accumulateTwoPhaseListeners} from './DOMModernPluginEventSystem';
31
+import accumulateTwoPhaseListeners from './accumulateTwoPhaseListeners';
32
33
const END_KEYCODES = [9, 13, 27, 32]; // Tab, Return, Esc, Space
34
const START_KEYCODE = 229;
packages/react-dom/src/events/ChangeEventPlugin.js
+1
-1
@@ -27,9 +27,9 @@ import isEventSupported from './isEventSupported';
27
import {getNodeFromInstance} from '../client/ReactDOMComponentTree';
28
import {updateValueIfChanged} from '../client/inputValueTracking';
29
import {setDefaultValue} from '../client/ReactDOMInput';
30
-import {accumulateTwoPhaseListeners} from './DOMModernPluginEventSystem';
30
31
import {disableInputAttributeSyncing} from 'shared/ReactFeatureFlags';
32
+import accumulateTwoPhaseListeners from './accumulateTwoPhaseListeners';
33
34
const eventTypes = {
35
change: {
packages/react-dom/src/events/DOMModernPluginEventSystem.js
+1
-45
@@ -19,9 +19,8 @@ import {registrationNameDependencies} from 'legacy-events/EventPluginRegistry';
19
import {batchedEventUpdates} from 'legacy-events/ReactGenericBatching';
20
import {executeDispatchesInOrder} from 'legacy-events/EventPluginUtils';
21
import {plugins} from 'legacy-events/EventPluginRegistry';
22
-import getListener from 'legacy-events/getListener';
22
24
-import {HostRoot, HostPortal, HostComponent} from 'shared/ReactWorkTags';
23
+import {HostRoot, HostPortal} from 'shared/ReactWorkTags';
24
25
import {addTrappedEventListener} from './ReactDOMEventListener';
26
import getEventTarget from './getEventTarget';
@@ -315,46 +314,3 @@ export function attachElementListener(listener: ReactDOMListener): void {
314
export function detachElementListener(listener: ReactDOMListener): void {
315
// TODO
316
}
318
-
319
-export function accumulateTwoPhaseListeners(event: ReactSyntheticEvent): void {
320
- const phasedRegistrationNames = event.dispatchConfig.phasedRegistrationNames;
321
- if (phasedRegistrationNames == null) {
322
- return;
323
- }
324
- const {bubbled, captured} = phasedRegistrationNames;
325
- const dispatchListeners = [];
326
- const dispatchInstances = [];
327
- let node = event._targetInst;
328
- let hasListeners = false;
329
-
330
- // Accumulate all instances and listeners via the target -> root path.
331
- while (node !== null) {
332
- // We only care for listeners that are on HostComponents (i.e. <div>)
333
- if (node.tag === HostComponent) {
334
- // Standard React on* listeners, i.e. onClick prop
335
- const captureListener = getListener(node, captured);
336
- if (captureListener != null) {
337
- hasListeners = true;
338
- // Capture listeners/instances should go at the start, so we
339
- // unshift them to the start of the array.
340
- dispatchListeners.unshift(captureListener);
341
- dispatchInstances.unshift(node);
342
- }
343
- const bubbleListener = getListener(node, bubbled);
344
- if (bubbleListener != null) {
345
- hasListeners = true;
346
- // Bubble listeners/instances should go at the end, so we
347
- // push them to the end of the array.
348
- dispatchListeners.push(bubbleListener);
349
- dispatchInstances.push(node);
350
- }
351
- }
352
- node = node.return;
353
- }
354
- // To prevent allocation to the event unless we actually
355
- // have listeners we use the flag we would have set above.
356
- if (hasListeners) {
357
- event._dispatchListeners = dispatchListeners;
358
- event._dispatchInstances = dispatchInstances;
359
- }
360
-}
packages/react-dom/src/events/SelectEventPlugin.js
+1
-1
@@ -26,7 +26,7 @@ import {getNodeFromInstance} from '../client/ReactDOMComponentTree';
26
import {hasSelectionCapabilities} from '../client/ReactInputSelection';
27
import {DOCUMENT_NODE} from '../shared/HTMLNodeType';
28
import {isListeningToAllDependencies} from './DOMEventListenerMap';
29
-import {accumulateTwoPhaseListeners} from './DOMModernPluginEventSystem';
29
+import accumulateTwoPhaseListeners from './accumulateTwoPhaseListeners';
30
31
const skipSelectionChangeEvent =
32
canUseDOM && 'documentMode' in document && document.documentMode <= 11;
packages/react-dom/src/events/SimpleEventPlugin.js
+1
-1
@@ -36,7 +36,7 @@ import SyntheticTransitionEvent from './SyntheticTransitionEvent';
36
import SyntheticUIEvent from './SyntheticUIEvent';
37
import SyntheticWheelEvent from './SyntheticWheelEvent';
38
import getEventCharCode from './getEventCharCode';
39
-import {accumulateTwoPhaseListeners} from './DOMModernPluginEventSystem';
39
+import accumulateTwoPhaseListeners from './accumulateTwoPhaseListeners';
40
41
// Only used in DEV for exhaustiveness validation.
42
const knownHTMLTopLevelTypes: Array<DOMTopLevelEventType> = [
packages/react-dom/src/events/accumulateTwoPhaseListeners.js
new
+62
@@ -0,0 +1,62 @@
1
+/**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow
8
+ */
9
+
10
+import type {ReactSyntheticEvent} from 'legacy-events/ReactSyntheticEventType';
11
+
12
+import getListener from 'legacy-events/getListener';
13
+import {HostComponent} from 'shared/ReactWorkTags';
14
+
15
+export default function accumulateTwoPhaseListeners(
16
+ event: ReactSyntheticEvent,
17
+ skipTarget?: boolean,
18
+): void {
19
+ const phasedRegistrationNames = event.dispatchConfig.phasedRegistrationNames;
20
+ if (phasedRegistrationNames == null) {
21
+ return;
22
+ }
23
+ const {bubbled, captured} = phasedRegistrationNames;
24
+ const dispatchListeners = [];
25
+ const dispatchInstances = [];
26
+ let node = event._targetInst;
27
+
28
+ // If we skip the target, then start the node at the parent
29
+ // of the target.
30
+ if (skipTarget) {
31
+ node = node.return;
32
+ }
33
+
34
+ // Accumulate all instances and listeners via the target -> root path.
35
+ while (node !== null) {
36
+ // We only care for listeners that are on HostComponents (i.e. <div>)
37
+ if (node.tag === HostComponent) {
38
+ // Standard React on* listeners, i.e. onClick prop
39
+ const captureListener = getListener(node, captured);
40
+ if (captureListener != null) {
41
+ // Capture listeners/instances should go at the start, so we
42
+ // unshift them to the start of the array.
43
+ dispatchListeners.unshift(captureListener);
44
+ dispatchInstances.unshift(node);
45
+ }
46
+ const bubbleListener = getListener(node, bubbled);
47
+ if (bubbleListener != null) {
48
+ // Bubble listeners/instances should go at the end, so we
49
+ // push them to the end of the array.
50
+ dispatchListeners.push(bubbleListener);
51
+ dispatchInstances.push(node);
52
+ }
53
+ }
54
+ node = node.return;
55
+ }
56
+ // To prevent allocation to the event unless we actually
57
+ // have listeners we check the length of one of the arrays.
58
+ if (dispatchListeners.length > 0) {
59
+ event._dispatchListeners = dispatchListeners;
60
+ event._dispatchInstances = dispatchInstances;
61
+ }
62
+}
packages/react-dom/src/test-utils/ReactTestUtils.js
+124
-2
@@ -21,6 +21,9 @@ import {ELEMENT_NODE} from '../shared/HTMLNodeType';
21
import * as DOMTopLevelEventTypes from '../events/DOMTopLevelEventTypes';
22
import {PLUGIN_EVENT_SYSTEM} from 'legacy-events/EventSystemFlags';
23
import act from './ReactTestUtilsAct';
24
+import forEachAccumulated from 'legacy-events/forEachAccumulated';
25
+import accumulateInto from 'legacy-events/accumulateInto';
26
+import {traverseTwoPhase} from 'shared/ReactTreeTraversal';
27
28
const {findDOMNode} = ReactDOM;
29
// Keep in sync with ReactDOMUnstableNativeDependencies.js
@@ -33,8 +36,6 @@ const [
36
injectEventPluginsByName,
37
/* eslint-enable no-unused-vars */
38
eventNameDispatchConfigs,
36
- accumulateTwoPhaseDispatches,
37
- accumulateDirectDispatches,
39
enqueueStateRestore,
40
restoreStateIfNeeded,
41
dispatchEvent,
@@ -377,6 +378,127 @@ function nativeTouchData(x, y) {
378
};
379
}
380
381
+// Start of inline: the below functions were inlined from
382
+// EventPropagator.js, as they deviated from ReactDOM's newer
383
+// implementations.
384
+function isInteractive(tag) {
385
+ return (
386
+ tag === 'button' ||
387
+ tag === 'input' ||
388
+ tag === 'select' ||
389
+ tag === 'textarea'
390
+ );
391
+}
392
+
393
+function shouldPreventMouseEvent(name, type, props) {
394
+ switch (name) {
395
+ case 'onClick':
396
+ case 'onClickCapture':
397
+ case 'onDoubleClick':
398
+ case 'onDoubleClickCapture':
399
+ case 'onMouseDown':
400
+ case 'onMouseDownCapture':
401
+ case 'onMouseMove':
402
+ case 'onMouseMoveCapture':
403
+ case 'onMouseUp':
404
+ case 'onMouseUpCapture':
405
+ case 'onMouseEnter':
406
+ return !!(props.disabled && isInteractive(type));
407
+ default:
408
+ return false;
409
+ }
410
+}
411
+
412
+/**
413
+ * @param {object} inst The instance, which is the source of events.
414
+ * @param {string} registrationName Name of listener (e.g. `onClick`).
415
+ * @return {?function} The stored callback.
416
+ */
417
+function getListener(inst: Fiber, registrationName: string) {
418
+ let listener;
419
+
420
+ // TODO: shouldPreventMouseEvent is DOM-specific and definitely should not
421
+ // live here; needs to be moved to a better place soon
422
+ const stateNode = inst.stateNode;
423
+ if (!stateNode) {
424
+ // Work in progress (ex: onload events in incremental mode).
425
+ return null;
426
+ }
427
+ const props = getFiberCurrentPropsFromNode(stateNode);
428
+ if (!props) {
429
+ // Work in progress.
430
+ return null;
431
+ }
432
+ listener = props[registrationName];
433
+ if (shouldPreventMouseEvent(registrationName, inst.type, props)) {
434
+ return null;
435
+ }
436
+ invariant(
437
+ !listener || typeof listener === 'function',
438
+ 'Expected `%s` listener to be a function, instead got a value of `%s` type.',
439
+ registrationName,
440
+ typeof listener,
441
+ );
442
+ return listener;
443
+}
444
+
445
+function listenerAtPhase(inst, event, propagationPhase: PropagationPhases) {
446
+ const registrationName =
447
+ event.dispatchConfig.phasedRegistrationNames[propagationPhase];
448
+ return getListener(inst, registrationName);
449
+}
450
+
451
+function accumulateDispatches(inst, ignoredDirection, event) {
452
+ if (inst && event && event.dispatchConfig.registrationName) {
453
+ const registrationName = event.dispatchConfig.registrationName;
454
+ const listener = getListener(inst, registrationName);
455
+ if (listener) {
456
+ event._dispatchListeners = accumulateInto(
457
+ event._dispatchListeners,
458
+ listener,
459
+ );
460
+ event._dispatchInstances = accumulateInto(event._dispatchInstances, inst);
461
+ }
462
+ }
463
+}
464
+
465
+function accumulateDirectionalDispatches(inst, phase, event) {
466
+ if (__DEV__) {
467
+ if (!inst) {
468
+ console.error('Dispatching inst must not be null');
469
+ }
470
+ }
471
+ const listener = listenerAtPhase(inst, event, phase);
472
+ if (listener) {
473
+ event._dispatchListeners = accumulateInto(
474
+ event._dispatchListeners,
475
+ listener,
476
+ );
477
+ event._dispatchInstances = accumulateInto(event._dispatchInstances, inst);
478
+ }
479
+}
480
+
481
+function accumulateDirectDispatchesSingle(event) {
482
+ if (event && event.dispatchConfig.registrationName) {
483
+ accumulateDispatches(event._targetInst, null, event);
484
+ }
485
+}
486
+
487
+function accumulateDirectDispatches(events) {
488
+ forEachAccumulated(events, accumulateDirectDispatchesSingle);
489
+}
490
+
491
+function accumulateTwoPhaseDispatchesSingle(event) {
492
+ if (event && event.dispatchConfig.phasedRegistrationNames) {
493
+ traverseTwoPhase(event._targetInst, accumulateDirectionalDispatches, event);
494
+ }
495
+}
496
+
497
+function accumulateTwoPhaseDispatches(events) {
498
+ forEachAccumulated(events, accumulateTwoPhaseDispatchesSingle);
499
+}
500
+// End of inline
501
+
502
const Simulate = {};
503
const SimulateNative = {};
504
packages/react-dom/src/test-utils/ReactTestUtilsAct.js
-2
@@ -23,8 +23,6 @@ const [
23
getFiberCurrentPropsFromNode,
24
injectEventPluginsByName,
25
eventNameDispatchConfigs,
26
- accumulateTwoPhaseDispatches,
27
- accumulateDirectDispatches,
26
enqueueStateRestore,
27
restoreStateIfNeeded,
28
dispatchEvent,
packages/react-native-renderer/src/ReactNativeBridgeEventPlugin.js
+42
-4
@@ -9,22 +9,60 @@
9
10
import type {AnyNativeEvent} from 'legacy-events/PluginModuleType';
11
import type {EventSystemFlags} from 'legacy-events/EventSystemFlags';
12
-import {
13
- accumulateTwoPhaseDispatches,
14
- accumulateDirectDispatches,
15
-} from 'legacy-events/EventPropagators';
12
+import {accumulateDirectDispatches} from 'legacy-events/EventPropagators';
13
import type {TopLevelType} from 'legacy-events/TopLevelEventTypes';
14
import SyntheticEvent from 'legacy-events/SyntheticEvent';
15
import invariant from 'shared/invariant';
16
17
// Module provided by RN:
18
import {ReactNativeViewConfigRegistry} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface';
19
+import accumulateInto from 'legacy-events/accumulateInto';
20
+import getListener from 'legacy-events/getListener';
21
+import {traverseTwoPhase} from 'shared/ReactTreeTraversal';
22
+import forEachAccumulated from 'legacy-events/forEachAccumulated';
23
24
const {
25
customBubblingEventTypes,
26
customDirectEventTypes,
27
} = ReactNativeViewConfigRegistry;
28
29
+// Start of inline: the below functions were inlined from
30
+// EventPropagator.js, as they deviated from ReactDOM's newer
31
+// implementations.
32
+function listenerAtPhase(inst, event, propagationPhase: PropagationPhases) {
33
+ const registrationName =
34
+ event.dispatchConfig.phasedRegistrationNames[propagationPhase];
35
+ return getListener(inst, registrationName);
36
+}
37
+
38
+function accumulateDirectionalDispatches(inst, phase, event) {
39
+ if (__DEV__) {
40
+ if (!inst) {
41
+ console.error('Dispatching inst must not be null');
42
+ }
43
+ }
44
+ const listener = listenerAtPhase(inst, event, phase);
45
+ if (listener) {
46
+ event._dispatchListeners = accumulateInto(
47
+ event._dispatchListeners,
48
+ listener,
49
+ );
50
+ event._dispatchInstances = accumulateInto(event._dispatchInstances, inst);
51
+ }
52
+}
53
+
54
+function accumulateTwoPhaseDispatchesSingle(event) {
55
+ if (event && event.dispatchConfig.phasedRegistrationNames) {
56
+ traverseTwoPhase(event._targetInst, accumulateDirectionalDispatches, event);
57
+ }
58
+}
59
+
60
+function accumulateTwoPhaseDispatches(events) {
61
+ forEachAccumulated(events, accumulateTwoPhaseDispatchesSingle);
62
+}
63
+// End of inline
64
+type PropagationPhases = 'bubbled' | 'captured';
65
+
66
const ReactNativeBridgeEventPlugin = {
67
eventTypes: {},
68