Skip to content

Commit 30a998d

Browse files
authored
ModernEventSystem: refactor accumulateTwoPhaseListeners (facebook#18274)
1 parent 8fe066f commit 30a998d

File tree

7 files changed

+60
-10
lines changed

7 files changed

+60
-10
lines changed

packages/legacy-events/ReactSyntheticEventType.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,7 @@ export type ReactSyntheticEvent = {|
3131
nativeEventTarget: EventTarget,
3232
) => ReactSyntheticEvent,
3333
isPersistent: () => boolean,
34+
_dispatchInstances: null | Array<Fiber>,
35+
_dispatchListeners: null | Array<Function>,
36+
_targetInst: null | Fiber,
3437
|};

packages/legacy-events/SyntheticEvent.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ function SyntheticEvent(
7676
this.dispatchConfig = dispatchConfig;
7777
this._targetInst = targetInst;
7878
this.nativeEvent = nativeEvent;
79+
this._dispatchListeners = null;
80+
this._dispatchInstances = null;
7981

8082
const Interface = this.constructor.Interface;
8183
for (const propName in Interface) {

packages/react-dom/src/events/BeforeInputEventPlugin.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
import type {TopLevelType} from 'legacy-events/TopLevelEventTypes';
99

10-
import {accumulateTwoPhaseDispatchesSingle} from 'legacy-events/EventPropagators';
1110
import {canUseDOM} from 'shared/ExecutionEnvironment';
1211

1312
import {
@@ -29,6 +28,7 @@ import {
2928
} from './FallbackCompositionState';
3029
import SyntheticCompositionEvent from './SyntheticCompositionEvent';
3130
import SyntheticInputEvent from './SyntheticInputEvent';
31+
import {accumulateTwoPhaseListeners} from './DOMModernPluginEventSystem';
3232

3333
const END_KEYCODES = [9, 13, 27, 32]; // Tab, Return, Esc, Space
3434
const START_KEYCODE = 229;
@@ -276,7 +276,7 @@ function extractCompositionEvent(
276276
}
277277
}
278278

279-
accumulateTwoPhaseDispatchesSingle(event);
279+
accumulateTwoPhaseListeners(event);
280280
return event;
281281
}
282282

@@ -437,7 +437,7 @@ function extractBeforeInputEvent(
437437
);
438438

439439
event.data = chars;
440-
accumulateTwoPhaseDispatchesSingle(event);
440+
accumulateTwoPhaseListeners(event);
441441
return event;
442442
}
443443

packages/react-dom/src/events/ChangeEventPlugin.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
*/
77

88
import {runEventsInBatch} from 'legacy-events/EventBatching';
9-
import {accumulateTwoPhaseDispatchesSingle} from 'legacy-events/EventPropagators';
109
import {enqueueStateRestore} from 'legacy-events/ReactControlledComponent';
1110
import {batchedUpdates} from 'legacy-events/ReactGenericBatching';
1211
import SyntheticEvent from 'legacy-events/SyntheticEvent';
@@ -28,6 +27,8 @@ import isEventSupported from './isEventSupported';
2827
import {getNodeFromInstance} from '../client/ReactDOMComponentTree';
2928
import {updateValueIfChanged} from '../client/inputValueTracking';
3029
import {setDefaultValue} from '../client/ReactDOMInput';
30+
import {accumulateTwoPhaseListeners} from './DOMModernPluginEventSystem';
31+
3132
import {disableInputAttributeSyncing} from 'shared/ReactFeatureFlags';
3233

3334
const eventTypes = {
@@ -59,7 +60,7 @@ function createAndAccumulateChangeEvent(inst, nativeEvent, target) {
5960
event.type = 'change';
6061
// Flag this event loop as needing state restore.
6162
enqueueStateRestore(target);
62-
accumulateTwoPhaseDispatchesSingle(event);
63+
accumulateTwoPhaseListeners(event);
6364
return event;
6465
}
6566
/**

packages/react-dom/src/events/DOMModernPluginEventSystem.js

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@ import {registrationNameDependencies} from 'legacy-events/EventPluginRegistry';
1919
import {batchedEventUpdates} from 'legacy-events/ReactGenericBatching';
2020
import {executeDispatchesInOrder} from 'legacy-events/EventPluginUtils';
2121
import {plugins} from 'legacy-events/EventPluginRegistry';
22+
import getListener from 'legacy-events/getListener';
2223

23-
import {HostRoot, HostPortal} from 'shared/ReactWorkTags';
24+
import {HostRoot, HostPortal, HostComponent} from 'shared/ReactWorkTags';
2425

2526
import {addTrappedEventListener} from './ReactDOMEventListener';
2627
import getEventTarget from './getEventTarget';
@@ -305,3 +306,46 @@ export function attachElementListener(listener: ReactDOMListener): void {
305306
export function detachElementListener(listener: ReactDOMListener): void {
306307
// TODO
307308
}
309+
310+
export function accumulateTwoPhaseListeners(event: ReactSyntheticEvent): void {
311+
const phasedRegistrationNames = event.dispatchConfig.phasedRegistrationNames;
312+
if (phasedRegistrationNames == null) {
313+
return;
314+
}
315+
const {bubbled, captured} = phasedRegistrationNames;
316+
const dispatchListeners = [];
317+
const dispatchInstances = [];
318+
let node = event._targetInst;
319+
let hasListeners = false;
320+
321+
// Accumulate all instances and listeners via the target -> root path.
322+
while (node !== null) {
323+
// We only care for listeners that are on HostComponents (i.e. <div>)
324+
if (node.tag === HostComponent) {
325+
// Standard React on* listeners, i.e. onClick prop
326+
const captureListener = getListener(node, captured);
327+
if (captureListener != null) {
328+
hasListeners = true;
329+
// Capture listeners/instances should go at the start, so we
330+
// unshift them to the start of the array.
331+
dispatchListeners.unshift(captureListener);
332+
dispatchInstances.unshift(node);
333+
}
334+
const bubbleListener = getListener(node, bubbled);
335+
if (bubbleListener != null) {
336+
hasListeners = true;
337+
// Bubble listeners/instances should go at the end, so we
338+
// push them to the end of the array.
339+
dispatchListeners.push(bubbleListener);
340+
dispatchInstances.push(node);
341+
}
342+
}
343+
node = node.return;
344+
}
345+
// To prevent allocation to the event unless we actually
346+
// have listeners we use the flag we would have set above.
347+
if (hasListeners) {
348+
event._dispatchListeners = dispatchListeners;
349+
event._dispatchInstances = dispatchInstances;
350+
}
351+
}

packages/react-dom/src/events/SelectEventPlugin.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
* LICENSE file in the root directory of this source tree.
66
*/
77

8-
import {accumulateTwoPhaseDispatchesSingle} from 'legacy-events/EventPropagators';
98
import {canUseDOM} from 'shared/ExecutionEnvironment';
109
import SyntheticEvent from 'legacy-events/SyntheticEvent';
1110
import isTextInputElement from 'shared/isTextInputElement';
@@ -27,6 +26,7 @@ import {getNodeFromInstance} from '../client/ReactDOMComponentTree';
2726
import {hasSelectionCapabilities} from '../client/ReactInputSelection';
2827
import {DOCUMENT_NODE} from '../shared/HTMLNodeType';
2928
import {isListeningToAllDependencies} from './DOMEventListenerMap';
29+
import {accumulateTwoPhaseListeners} from './DOMModernPluginEventSystem';
3030

3131
const skipSelectionChangeEvent =
3232
canUseDOM && 'documentMode' in document && document.documentMode <= 11;
@@ -135,7 +135,7 @@ function constructSelectEvent(nativeEvent, nativeEventTarget) {
135135
syntheticEvent.type = 'select';
136136
syntheticEvent.target = activeElement;
137137

138-
accumulateTwoPhaseDispatchesSingle(syntheticEvent);
138+
accumulateTwoPhaseListeners(syntheticEvent);
139139

140140
return syntheticEvent;
141141
}

packages/react-dom/src/events/SimpleEventPlugin.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import type {Fiber} from 'react-reconciler/src/ReactFiber';
1616
import type {PluginModule} from 'legacy-events/PluginModuleType';
1717
import type {EventSystemFlags} from 'legacy-events/EventSystemFlags';
1818

19-
import {accumulateTwoPhaseDispatchesSingle} from 'legacy-events/EventPropagators';
2019
import SyntheticEvent from 'legacy-events/SyntheticEvent';
2120

2221
import * as DOMTopLevelEventTypes from './DOMTopLevelEventTypes';
@@ -37,6 +36,7 @@ import SyntheticTransitionEvent from './SyntheticTransitionEvent';
3736
import SyntheticUIEvent from './SyntheticUIEvent';
3837
import SyntheticWheelEvent from './SyntheticWheelEvent';
3938
import getEventCharCode from './getEventCharCode';
39+
import {accumulateTwoPhaseListeners} from './DOMModernPluginEventSystem';
4040

4141
// Only used in DEV for exhaustiveness validation.
4242
const knownHTMLTopLevelTypes: Array<DOMTopLevelEventType> = [
@@ -191,7 +191,7 @@ const SimpleEventPlugin: PluginModule<MouseEvent> = {
191191
nativeEvent,
192192
nativeEventTarget,
193193
);
194-
accumulateTwoPhaseDispatchesSingle(event);
194+
accumulateTwoPhaseListeners(event);
195195
return event;
196196
},
197197
};

0 commit comments

Comments
 (0)