Skip to content

Commit

Permalink
ModernEventSystem: refactor accumulateTwoPhaseListeners (#18274)
Browse files Browse the repository at this point in the history
  • Loading branch information
trueadm authored Mar 11, 2020
1 parent 8fe066f commit 30a998d
Show file tree
Hide file tree
Showing 7 changed files with 60 additions and 10 deletions.
3 changes: 3 additions & 0 deletions packages/legacy-events/ReactSyntheticEventType.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,7 @@ export type ReactSyntheticEvent = {|
nativeEventTarget: EventTarget,
) => ReactSyntheticEvent,
isPersistent: () => boolean,
_dispatchInstances: null | Array<Fiber>,
_dispatchListeners: null | Array<Function>,
_targetInst: null | Fiber,
|};
2 changes: 2 additions & 0 deletions packages/legacy-events/SyntheticEvent.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ function SyntheticEvent(
this.dispatchConfig = dispatchConfig;
this._targetInst = targetInst;
this.nativeEvent = nativeEvent;
this._dispatchListeners = null;
this._dispatchInstances = null;

const Interface = this.constructor.Interface;
for (const propName in Interface) {
Expand Down
6 changes: 3 additions & 3 deletions packages/react-dom/src/events/BeforeInputEventPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

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

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

import {
Expand All @@ -29,6 +28,7 @@ import {
} from './FallbackCompositionState';
import SyntheticCompositionEvent from './SyntheticCompositionEvent';
import SyntheticInputEvent from './SyntheticInputEvent';
import {accumulateTwoPhaseListeners} from './DOMModernPluginEventSystem';

const END_KEYCODES = [9, 13, 27, 32]; // Tab, Return, Esc, Space
const START_KEYCODE = 229;
Expand Down Expand Up @@ -276,7 +276,7 @@ function extractCompositionEvent(
}
}

accumulateTwoPhaseDispatchesSingle(event);
accumulateTwoPhaseListeners(event);
return event;
}

Expand Down Expand Up @@ -437,7 +437,7 @@ function extractBeforeInputEvent(
);

event.data = chars;
accumulateTwoPhaseDispatchesSingle(event);
accumulateTwoPhaseListeners(event);
return event;
}

Expand Down
5 changes: 3 additions & 2 deletions packages/react-dom/src/events/ChangeEventPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
*/

import {runEventsInBatch} from 'legacy-events/EventBatching';
import {accumulateTwoPhaseDispatchesSingle} from 'legacy-events/EventPropagators';
import {enqueueStateRestore} from 'legacy-events/ReactControlledComponent';
import {batchedUpdates} from 'legacy-events/ReactGenericBatching';
import SyntheticEvent from 'legacy-events/SyntheticEvent';
Expand All @@ -28,6 +27,8 @@ import isEventSupported from './isEventSupported';
import {getNodeFromInstance} from '../client/ReactDOMComponentTree';
import {updateValueIfChanged} from '../client/inputValueTracking';
import {setDefaultValue} from '../client/ReactDOMInput';
import {accumulateTwoPhaseListeners} from './DOMModernPluginEventSystem';

import {disableInputAttributeSyncing} from 'shared/ReactFeatureFlags';

const eventTypes = {
Expand Down Expand Up @@ -59,7 +60,7 @@ function createAndAccumulateChangeEvent(inst, nativeEvent, target) {
event.type = 'change';
// Flag this event loop as needing state restore.
enqueueStateRestore(target);
accumulateTwoPhaseDispatchesSingle(event);
accumulateTwoPhaseListeners(event);
return event;
}
/**
Expand Down
46 changes: 45 additions & 1 deletion packages/react-dom/src/events/DOMModernPluginEventSystem.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ import {registrationNameDependencies} from 'legacy-events/EventPluginRegistry';
import {batchedEventUpdates} from 'legacy-events/ReactGenericBatching';
import {executeDispatchesInOrder} from 'legacy-events/EventPluginUtils';
import {plugins} from 'legacy-events/EventPluginRegistry';
import getListener from 'legacy-events/getListener';

import {HostRoot, HostPortal} from 'shared/ReactWorkTags';
import {HostRoot, HostPortal, HostComponent} from 'shared/ReactWorkTags';

import {addTrappedEventListener} from './ReactDOMEventListener';
import getEventTarget from './getEventTarget';
Expand Down Expand Up @@ -305,3 +306,46 @@ export function attachElementListener(listener: ReactDOMListener): void {
export function detachElementListener(listener: ReactDOMListener): void {
// TODO
}

export function accumulateTwoPhaseListeners(event: ReactSyntheticEvent): void {
const phasedRegistrationNames = event.dispatchConfig.phasedRegistrationNames;
if (phasedRegistrationNames == null) {
return;
}
const {bubbled, captured} = phasedRegistrationNames;
const dispatchListeners = [];
const dispatchInstances = [];
let node = event._targetInst;
let hasListeners = false;

// Accumulate all instances and listeners via the target -> root path.
while (node !== null) {
// We only care for listeners that are on HostComponents (i.e. <div>)
if (node.tag === HostComponent) {
// Standard React on* listeners, i.e. onClick prop
const captureListener = getListener(node, captured);
if (captureListener != null) {
hasListeners = true;
// Capture listeners/instances should go at the start, so we
// unshift them to the start of the array.
dispatchListeners.unshift(captureListener);
dispatchInstances.unshift(node);
}
const bubbleListener = getListener(node, bubbled);
if (bubbleListener != null) {
hasListeners = true;
// Bubble listeners/instances should go at the end, so we
// push them to the end of the array.
dispatchListeners.push(bubbleListener);
dispatchInstances.push(node);
}
}
node = node.return;
}
// To prevent allocation to the event unless we actually
// have listeners we use the flag we would have set above.
if (hasListeners) {
event._dispatchListeners = dispatchListeners;
event._dispatchInstances = dispatchInstances;
}
}
4 changes: 2 additions & 2 deletions packages/react-dom/src/events/SelectEventPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
* LICENSE file in the root directory of this source tree.
*/

import {accumulateTwoPhaseDispatchesSingle} from 'legacy-events/EventPropagators';
import {canUseDOM} from 'shared/ExecutionEnvironment';
import SyntheticEvent from 'legacy-events/SyntheticEvent';
import isTextInputElement from 'shared/isTextInputElement';
Expand All @@ -27,6 +26,7 @@ import {getNodeFromInstance} from '../client/ReactDOMComponentTree';
import {hasSelectionCapabilities} from '../client/ReactInputSelection';
import {DOCUMENT_NODE} from '../shared/HTMLNodeType';
import {isListeningToAllDependencies} from './DOMEventListenerMap';
import {accumulateTwoPhaseListeners} from './DOMModernPluginEventSystem';

const skipSelectionChangeEvent =
canUseDOM && 'documentMode' in document && document.documentMode <= 11;
Expand Down Expand Up @@ -135,7 +135,7 @@ function constructSelectEvent(nativeEvent, nativeEventTarget) {
syntheticEvent.type = 'select';
syntheticEvent.target = activeElement;

accumulateTwoPhaseDispatchesSingle(syntheticEvent);
accumulateTwoPhaseListeners(syntheticEvent);

return syntheticEvent;
}
Expand Down
4 changes: 2 additions & 2 deletions packages/react-dom/src/events/SimpleEventPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import type {Fiber} from 'react-reconciler/src/ReactFiber';
import type {PluginModule} from 'legacy-events/PluginModuleType';
import type {EventSystemFlags} from 'legacy-events/EventSystemFlags';

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

import * as DOMTopLevelEventTypes from './DOMTopLevelEventTypes';
Expand All @@ -37,6 +36,7 @@ import SyntheticTransitionEvent from './SyntheticTransitionEvent';
import SyntheticUIEvent from './SyntheticUIEvent';
import SyntheticWheelEvent from './SyntheticWheelEvent';
import getEventCharCode from './getEventCharCode';
import {accumulateTwoPhaseListeners} from './DOMModernPluginEventSystem';

// Only used in DEV for exhaustiveness validation.
const knownHTMLTopLevelTypes: Array<DOMTopLevelEventType> = [
Expand Down Expand Up @@ -191,7 +191,7 @@ const SimpleEventPlugin: PluginModule<MouseEvent> = {
nativeEvent,
nativeEventTarget,
);
accumulateTwoPhaseDispatchesSingle(event);
accumulateTwoPhaseListeners(event);
return event;
},
};
Expand Down

0 comments on commit 30a998d

Please sign in to comment.