Make event config an implementation detail of each plugin (#19236)

* Merge two variables with same purpose

* Replace dispatchConfig with _reactName on event object
This commit is contained in:
Dan Abramov 2020-07-02 00:03:17 +01:00 committed by GitHub
parent b683c07ccc
commit 991c3b8193
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 71 additions and 176 deletions

View File

@ -8,7 +8,7 @@
*/
import {
registrationNames,
registrationNameDependencies,
possibleRegistrationNames,
} from '../events/EventPluginRegistry';
import {canUseDOM} from 'shared/ExecutionEnvironment';
@ -133,7 +133,7 @@ if (__DEV__) {
validateARIAProperties(type, props);
validateInputProperties(type, props);
validateUnknownProperties(type, props, {
registrationNames,
registrationNameDependencies,
possibleRegistrationNames,
});
};
@ -356,7 +356,7 @@ function setInitialDOMProperties(
// We could have excluded it in the property list instead of
// adding a special case here, but then it wouldn't be emitted
// on server rendering (but we *do* want to emit it in SSR).
} else if (registrationNames.hasOwnProperty(propKey)) {
} else if (registrationNameDependencies.hasOwnProperty(propKey)) {
if (nextProp != null) {
if (__DEV__ && typeof nextProp !== 'function') {
warnForInvalidEventListener(propKey, nextProp);
@ -694,7 +694,7 @@ export function diffProperties(
// Noop
} else if (propKey === AUTOFOCUS) {
// Noop. It doesn't work on updates anyway.
} else if (registrationNames.hasOwnProperty(propKey)) {
} else if (registrationNameDependencies.hasOwnProperty(propKey)) {
// This is a special case. If any listener updates we need to ensure
// that the "current" fiber pointer gets updated so we need a commit
// to update this element.
@ -781,7 +781,7 @@ export function diffProperties(
propKey === SUPPRESS_HYDRATION_WARNING
) {
// Noop
} else if (registrationNames.hasOwnProperty(propKey)) {
} else if (registrationNameDependencies.hasOwnProperty(propKey)) {
if (nextProp != null) {
// We eagerly listen to this even though we haven't committed yet.
if (__DEV__ && typeof nextProp !== 'function') {
@ -978,7 +978,7 @@ export function diffHydratedProperties(
updatePayload = [CHILDREN, '' + nextProp];
}
}
} else if (registrationNames.hasOwnProperty(propKey)) {
} else if (registrationNameDependencies.hasOwnProperty(propKey)) {
if (nextProp != null) {
if (__DEV__ && typeof nextProp !== 'function') {
warnForInvalidEventListener(propKey, nextProp);

View File

@ -13,10 +13,6 @@ import type {
DOMTopLevelEventType,
} from '../events/TopLevelEventTypes';
import type {EventTypes} from '../events/PluginModuleType';
import type {
DispatchConfig,
CustomDispatchConfig,
} from '../events/ReactSyntheticEventType';
import * as DOMTopLevelEventTypes from './DOMTopLevelEventTypes';
import {
@ -35,9 +31,9 @@ import {enableCreateEventHandleAPI} from 'shared/ReactFeatureFlags';
// update the below line.
export const simpleEventPluginEventTypes: EventTypes = {};
export const topLevelEventsToDispatchConfig: Map<
export const topLevelEventsToReactNames: Map<
TopLevelType,
DispatchConfig | CustomDispatchConfig,
string | null,
> = new Map();
const eventPriorities = new Map();
@ -167,8 +163,8 @@ const continuousPairsForSimpleEventPlugin = [
* },
* ...
* };
* topLevelEventsToDispatchConfig = new Map([
* [TOP_ABORT, { sameConfig }],
* topLevelEventsToReactNames = new Map([
* [TOP_ABORT, 'onAbort'],
* ]);
*/
@ -197,7 +193,7 @@ function processSimpleEventPluginPairsByPriority(
eventPriority: priority,
};
eventPriorities.set(topEvent, priority);
topLevelEventsToDispatchConfig.set(topEvent, config);
topLevelEventsToReactNames.set(topEvent, onEvent);
simpleEventPluginEventTypes[event] = config;
}
}

View File

@ -16,10 +16,7 @@ import type {
DispatchQueueItemPhase,
DispatchQueueItemPhaseEntry,
} from './PluginModuleType';
import type {
ReactSyntheticEvent,
CustomDispatchConfig,
} from './ReactSyntheticEventType';
import type {ReactSyntheticEvent} from './ReactSyntheticEventType';
import type {
ElementListenerMap,
ElementListenerMapEntry,
@ -113,7 +110,7 @@ import {
addEventCaptureListenerWithPassiveFlag,
} from './EventListener';
import {removeTrappedEventListener} from './DeprecatedDOMEventResponderSystem';
import {topLevelEventsToDispatchConfig} from './DOMEventProperties';
import {topLevelEventsToReactNames} from './DOMEventProperties';
import * as ModernBeforeInputEventPlugin from './plugins/ModernBeforeInputEventPlugin';
import * as ModernChangeEventPlugin from './plugins/ModernChangeEventPlugin';
import * as ModernEnterLeaveEventPlugin from './plugins/ModernEnterLeaveEventPlugin';
@ -223,14 +220,6 @@ if (enableCreateEventHandleAPI) {
capturePhaseEvents.add(TOP_AFTER_BLUR);
}
const emptyDispatchConfigForCustomEvents: CustomDispatchConfig = {
customEvent: true,
phasedRegistrationNames: {
bubbled: null,
captured: null,
},
};
function executeDispatch(
event: ReactSyntheticEvent,
listener: Function,
@ -639,11 +628,11 @@ export function accumulateTwoPhaseListeners(
event: ReactSyntheticEvent,
accumulateEventHandleListeners?: boolean,
): void {
const phasedRegistrationNames = event.dispatchConfig.phasedRegistrationNames;
const bubbled = event._reactName;
const captured = bubbled !== null ? bubbled + 'Capture' : null;
const capturePhase: DispatchQueueItemPhase = [];
const bubblePhase: DispatchQueueItemPhase = [];
const {bubbled, captured} = phasedRegistrationNames;
// If we are not handling EventTarget only phase, then we're doing the
// usual two phase accumulation using the React fiber tree to pick up
// all relevant useEvent and on* prop events.
@ -826,7 +815,7 @@ function accumulateEnterLeaveListenersForEvent(
common: Fiber | null,
capture: boolean,
): void {
const registrationName = event.dispatchConfig.registrationName;
const registrationName = event._reactName;
if (registrationName === undefined) {
return;
}
@ -944,17 +933,14 @@ export function accumulateEventTargetListeners(
}
export function addEventTypeToDispatchConfig(type: DOMTopLevelEventType): void {
const dispatchConfig = topLevelEventsToDispatchConfig.get(type);
// If we don't have a dispatchConfig, then we're dealing with
const reactName = topLevelEventsToReactNames.get(type);
// If we don't have a reactName, then we're dealing with
// an event type that React does not know about (i.e. a custom event).
// We need to register an event config for this or the SimpleEventPlugin
// will not appropriately provide a SyntheticEvent, so we use out empty
// dispatch config for custom events.
if (dispatchConfig === undefined) {
topLevelEventsToDispatchConfig.set(
type,
emptyDispatchConfigForCustomEvents,
);
if (reactName === undefined) {
topLevelEventsToReactNames.set(type, null);
}
}

View File

@ -10,13 +10,6 @@
import type {TopLevelType} from './TopLevelEventTypes';
import type {EventTypes} from './PluginModuleType';
import invariant from 'shared/invariant';
/**
* Mapping from registration name to plugin module
*/
export const registrationNames = {};
/**
* Mapping from registration name to event name
*/
@ -62,13 +55,16 @@ function publishRegistrationName(
registrationName: string,
dependencies: ?Array<TopLevelType>,
): void {
invariant(
!registrationNames[registrationName],
'EventPluginRegistry: More than one plugin attempted to publish the same ' +
'registration name, `%s`.',
registrationName,
);
registrationNames[registrationName] = true;
if (__DEV__) {
if (registrationNameDependencies[registrationName]) {
console.error(
'EventPluginRegistry: More than one plugin attempted to publish the same ' +
'registration name, `%s`.',
registrationName,
);
}
}
registrationNameDependencies[registrationName] = dependencies;
if (__DEV__) {

View File

@ -22,21 +22,12 @@ export type DispatchConfig = {|
eventPriority?: EventPriority,
|};
export type CustomDispatchConfig = {|
phasedRegistrationNames: {|
bubbled: null,
captured: null,
|},
registrationName?: string,
customEvent: true,
|};
export type ReactSyntheticEvent = {|
dispatchConfig: DispatchConfig | CustomDispatchConfig,
isPersistent: () => boolean,
isPropagationStopped: () => boolean,
_dispatchInstances?: null | Array<Fiber | null> | Fiber,
_dispatchListeners?: null | Array<Function> | Function,
_reactName: string,
_targetInst: Fiber,
type: string,
currentTarget: null | EventTarget,

View File

@ -48,19 +48,9 @@ function functionThatReturnsFalse() {
* Synthetic events (and subclasses) implement the DOM Level 3 Events API by
* normalizing browser quirks. Subclasses do not necessarily have to implement a
* DOM interface; custom application-specific events can also subclass this.
*
* @param {object} dispatchConfig Configuration used to dispatch this event.
* @param {*} targetInst Marker identifying the event target.
* @param {object} nativeEvent Native browser event.
* @param {DOMEventTarget} nativeEventTarget Target node.
*/
function SyntheticEvent(
dispatchConfig,
targetInst,
nativeEvent,
nativeEventTarget,
) {
this.dispatchConfig = dispatchConfig;
function SyntheticEvent(reactName, targetInst, nativeEvent, nativeEventTarget) {
this._reactName = reactName;
this._targetInst = targetInst;
this.nativeEvent = nativeEvent;

View File

@ -140,11 +140,11 @@ function isKeypressCommand(nativeEvent) {
function getCompositionEventType(topLevelType) {
switch (topLevelType) {
case TOP_COMPOSITION_START:
return eventTypes.compositionStart;
return 'onCompositionStart';
case TOP_COMPOSITION_END:
return eventTypes.compositionEnd;
return 'onCompositionEnd';
case TOP_COMPOSITION_UPDATE:
return eventTypes.compositionUpdate;
return 'onCompositionUpdate';
}
}
@ -237,10 +237,10 @@ function extractCompositionEvent(
eventType = getCompositionEventType(topLevelType);
} else if (!isComposing) {
if (isFallbackCompositionStart(topLevelType, nativeEvent)) {
eventType = eventTypes.compositionStart;
eventType = 'onCompositionStart';
}
} else if (isFallbackCompositionEnd(topLevelType, nativeEvent)) {
eventType = eventTypes.compositionEnd;
eventType = 'onCompositionEnd';
}
if (!eventType) {
@ -250,9 +250,9 @@ function extractCompositionEvent(
if (useFallbackCompositionData && !isUsingKoreanIME(nativeEvent)) {
// The current composition is stored statically and must not be
// overwritten while composition continues.
if (!isComposing && eventType === eventTypes.compositionStart) {
if (!isComposing && eventType === 'onCompositionStart') {
isComposing = FallbackCompositionStateInitialize(nativeEventTarget);
} else if (eventType === eventTypes.compositionEnd) {
} else if (eventType === 'onCompositionEnd') {
if (isComposing) {
fallbackData = FallbackCompositionStateGetData();
}
@ -430,7 +430,7 @@ function extractBeforeInputEvent(
}
const event = new SyntheticInputEvent(
eventTypes.beforeInput,
'onBeforeInput',
null,
nativeEvent,
nativeEventTarget,

View File

@ -64,12 +64,7 @@ function createAndAccumulateChangeEvent(
nativeEvent,
target,
) {
const event = new SyntheticEvent(
eventTypes.change,
null,
nativeEvent,
target,
);
const event = new SyntheticEvent('onChange', null, nativeEvent, target);
event.type = 'change';
// Flag this event loop as needing state restore.
enqueueStateRestore(((target: any): Node));

View File

@ -126,16 +126,16 @@ function extractEvents(
if (topLevelType === TOP_MOUSE_OUT || topLevelType === TOP_MOUSE_OVER) {
eventInterface = SyntheticMouseEvent;
leaveEventType = eventTypes.mouseLeave;
enterEventType = eventTypes.mouseEnter;
leaveEventType = 'onMouseLeave';
enterEventType = 'onMouseEnter';
eventTypePrefix = 'mouse';
} else if (
topLevelType === TOP_POINTER_OUT ||
topLevelType === TOP_POINTER_OVER
) {
eventInterface = SyntheticPointerEvent;
leaveEventType = eventTypes.pointerLeave;
enterEventType = eventTypes.pointerEnter;
leaveEventType = 'onPointerLeave';
enterEventType = 'onPointerEnter';
eventTypePrefix = 'pointer';
}

View File

@ -133,7 +133,7 @@ function constructSelectEvent(dispatchQueue, nativeEvent, nativeEventTarget) {
lastSelection = currentSelection;
const syntheticEvent = new SyntheticEvent(
eventTypes.select,
'onSelect',
null,
nativeEvent,
nativeEventTarget,

View File

@ -7,10 +7,7 @@
* @flow
*/
import type {
TopLevelType,
DOMTopLevelEventType,
} from '../../events/TopLevelEventTypes';
import type {TopLevelType} from '../../events/TopLevelEventTypes';
import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
import type {
AnyNativeEvent,
@ -22,7 +19,7 @@ import SyntheticEvent from '../../events/SyntheticEvent';
import * as DOMTopLevelEventTypes from '../DOMTopLevelEventTypes';
import {
topLevelEventsToDispatchConfig,
topLevelEventsToReactNames,
simpleEventPluginEventTypes,
} from '../DOMEventProperties';
import {
@ -46,41 +43,6 @@ import getEventCharCode from '../getEventCharCode';
import {enableCreateEventHandleAPI} from 'shared/ReactFeatureFlags';
// Only used in DEV for exhaustiveness validation.
const knownHTMLTopLevelTypes: Array<DOMTopLevelEventType> = [
DOMTopLevelEventTypes.TOP_ABORT,
DOMTopLevelEventTypes.TOP_CANCEL,
DOMTopLevelEventTypes.TOP_CAN_PLAY,
DOMTopLevelEventTypes.TOP_CAN_PLAY_THROUGH,
DOMTopLevelEventTypes.TOP_CLOSE,
DOMTopLevelEventTypes.TOP_DURATION_CHANGE,
DOMTopLevelEventTypes.TOP_EMPTIED,
DOMTopLevelEventTypes.TOP_ENCRYPTED,
DOMTopLevelEventTypes.TOP_ENDED,
DOMTopLevelEventTypes.TOP_ERROR,
DOMTopLevelEventTypes.TOP_INPUT,
DOMTopLevelEventTypes.TOP_INVALID,
DOMTopLevelEventTypes.TOP_LOAD,
DOMTopLevelEventTypes.TOP_LOADED_DATA,
DOMTopLevelEventTypes.TOP_LOADED_METADATA,
DOMTopLevelEventTypes.TOP_LOAD_START,
DOMTopLevelEventTypes.TOP_PAUSE,
DOMTopLevelEventTypes.TOP_PLAY,
DOMTopLevelEventTypes.TOP_PLAYING,
DOMTopLevelEventTypes.TOP_PROGRESS,
DOMTopLevelEventTypes.TOP_RATE_CHANGE,
DOMTopLevelEventTypes.TOP_RESET,
DOMTopLevelEventTypes.TOP_SEEKED,
DOMTopLevelEventTypes.TOP_SEEKING,
DOMTopLevelEventTypes.TOP_STALLED,
DOMTopLevelEventTypes.TOP_SUBMIT,
DOMTopLevelEventTypes.TOP_SUSPEND,
DOMTopLevelEventTypes.TOP_TIME_UPDATE,
DOMTopLevelEventTypes.TOP_TOGGLE,
DOMTopLevelEventTypes.TOP_VOLUME_CHANGE,
DOMTopLevelEventTypes.TOP_WAITING,
];
function extractEvents(
dispatchQueue: DispatchQueue,
topLevelType: TopLevelType,
@ -90,8 +52,8 @@ function extractEvents(
eventSystemFlags: EventSystemFlags,
targetContainer: null | EventTarget,
): void {
const dispatchConfig = topLevelEventsToDispatchConfig.get(topLevelType);
if (!dispatchConfig) {
const reactName = topLevelEventsToReactNames.get(topLevelType);
if (reactName === undefined) {
return;
}
let EventConstructor;
@ -179,25 +141,12 @@ function extractEvents(
EventConstructor = SyntheticPointerEvent;
break;
default:
if (__DEV__) {
if (
knownHTMLTopLevelTypes.indexOf(topLevelType) === -1 &&
dispatchConfig.customEvent !== true
) {
console.error(
'SimpleEventPlugin: Unhandled event type, `%s`. This warning ' +
'is likely caused by a bug in React. Please file an issue.',
topLevelType,
);
}
}
// HTML Events
// @see http://www.w3.org/TR/html5/index.html#events-0
// Unknown event. This is used by createEventHandle.
EventConstructor = SyntheticEvent;
break;
}
const event = new EventConstructor(
dispatchConfig,
reactName,
null,
nativeEvent,
nativeEventTarget,

View File

@ -43,8 +43,11 @@ if (__DEV__) {
// We can't rely on the event system being injected on the server.
if (eventRegistry != null) {
const {registrationNames, possibleRegistrationNames} = eventRegistry;
if (registrationNames.hasOwnProperty(name)) {
const {
registrationNameDependencies,
possibleRegistrationNames,
} = eventRegistry;
if (registrationNameDependencies.hasOwnProperty(name)) {
return true;
}
const registrationName = possibleRegistrationNames.hasOwnProperty(

View File

@ -491,14 +491,16 @@ function getListener(inst: Fiber, registrationName: string) {
}
function listenerAtPhase(inst, event, propagationPhase: PropagationPhases) {
const registrationName =
event.dispatchConfig.phasedRegistrationNames[propagationPhase];
let registrationName = event._reactName;
if (propagationPhase === 'captured') {
registrationName += 'Capture';
}
return getListener(inst, registrationName);
}
function accumulateDispatches(inst, ignoredDirection, event) {
if (inst && event && event.dispatchConfig.registrationName) {
const registrationName = event.dispatchConfig.registrationName;
if (inst && event && event._reactName) {
const registrationName = event._reactName;
const listener = getListener(inst, registrationName);
if (listener) {
if (event._dispatchListeners == null) {
@ -533,13 +535,13 @@ function accumulateDirectionalDispatches(inst, phase, event) {
}
function accumulateDirectDispatchesSingle(event) {
if (event && event.dispatchConfig.registrationName) {
if (event && event._reactName) {
accumulateDispatches(event._targetInst, null, event);
}
}
function accumulateTwoPhaseDispatchesSingle(event) {
if (event && event.dispatchConfig.phasedRegistrationNames) {
if (event && event._reactName) {
traverseTwoPhase(event._targetInst, accumulateDirectionalDispatches, event);
}
}
@ -577,27 +579,14 @@ function makeSimulator(eventType) {
'a component instance. Pass the DOM node you wish to simulate the event on instead.',
);
// Reconstruct more or less what the original event system produced.
// We could remove this indirection here but we also don't plan to invest in Simulate anyway.
const dispatchConfig = {};
if (directDispatchEventTypes.has(eventType)) {
dispatchConfig.registrationName =
'on' + eventType[0].toUpperCase() + eventType.slice(1);
} else {
dispatchConfig.phasedRegistrationNames = {
bubbled: 'on' + eventType[0].toUpperCase() + eventType.slice(1),
captured:
'on' + eventType[0].toUpperCase() + eventType.slice(1) + 'Capture',
};
}
const reactName = 'on' + eventType[0].toUpperCase() + eventType.slice(1);
const fakeNativeEvent = new Event();
fakeNativeEvent.target = domNode;
fakeNativeEvent.type = eventType.toLowerCase();
const targetInst = getInstanceFromNode(domNode);
const event = new SyntheticEvent(
dispatchConfig,
reactName,
targetInst,
fakeNativeEvent,
domNode,
@ -608,10 +597,10 @@ function makeSimulator(eventType) {
event.persist();
Object.assign(event, eventData);
if (dispatchConfig.phasedRegistrationNames) {
accumulateTwoPhaseDispatchesSingle(event);
} else {
if (directDispatchEventTypes.has(eventType)) {
accumulateDirectDispatchesSingle(event);
} else {
accumulateTwoPhaseDispatchesSingle(event);
}
ReactDOM.unstable_batchedUpdates(function() {