@samitouri / QOS-React-2 / commits / d3ec42020d

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