Remove dead code from modern event system (#19233)

* Remove dead code from modern event system

* Remove SSR dependency on EventPluginRegistry
This commit is contained in:
Dan Abramov
2020-07-01 21:04:14 +01:00
committed by GitHub
parent 9fba65efa5
commit 75b6921d64
38 changed files with 237 additions and 755 deletions

View File

@@ -11,7 +11,6 @@ import type {ReactNodeList} from 'shared/ReactTypes';
import type {Container} from './ReactDOMHostConfig';
import '../shared/checkReact';
import './ReactDOMClientInjection';
import {
findDOMNode,
render,
@@ -39,10 +38,7 @@ import {
} from 'react-reconciler/src/ReactFiberReconciler';
import {createPortal as createPortalImpl} from 'react-reconciler/src/ReactPortal';
import {canUseDOM} from 'shared/ExecutionEnvironment';
import {
eventNameDispatchConfigs,
injectEventPluginsByName,
} from '../legacy-events/EventPluginRegistry';
import {eventNameDispatchConfigs} from '../events/EventPluginRegistry';
import ReactVersion from 'shared/ReactVersion';
import invariant from 'shared/invariant';
import {
@@ -57,7 +53,6 @@ import {
getClosestInstanceFromNode,
} from './ReactDOMComponentTree';
import {restoreControlledState} from './ReactDOMComponent';
import {dispatchEvent} from '../events/ReactDOMEventListener';
import {
setAttemptSynchronousHydration,
setAttemptUserBlockingHydration,
@@ -180,11 +175,9 @@ const Internals = {
getInstanceFromNode,
getNodeFromInstance,
getFiberCurrentPropsFromNode,
injectEventPluginsByName,
eventNameDispatchConfigs,
enqueueStateRestore,
restoreStateIfNeeded,
dispatchEvent,
flushPassiveEffects,
IsThisRendererActing,
],

View File

@@ -1,24 +0,0 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
// TODO: remove this injection altogether.
import ModernBeforeInputEventPlugin from '../events/plugins/ModernBeforeInputEventPlugin';
import ModernChangeEventPlugin from '../events/plugins/ModernChangeEventPlugin';
import ModernEnterLeaveEventPlugin from '../events/plugins/ModernEnterLeaveEventPlugin';
import ModernSelectEventPlugin from '../events/plugins/ModernSelectEventPlugin';
import ModernSimpleEventPlugin from '../events/plugins/ModernSimpleEventPlugin';
import {injectEventPlugins} from '../legacy-events/EventPluginRegistry';
injectEventPlugins([
ModernSimpleEventPlugin,
ModernEnterLeaveEventPlugin,
ModernChangeEventPlugin,
ModernSelectEventPlugin,
ModernBeforeInputEventPlugin,
]);

View File

@@ -7,7 +7,10 @@
* @flow
*/
import {registrationNameModules} from '../legacy-events/EventPluginRegistry';
import {
registrationNameModules,
possibleRegistrationNames,
} from '../events/EventPluginRegistry';
import {canUseDOM} from 'shared/ExecutionEnvironment';
import invariant from 'shared/invariant';
import {
@@ -129,7 +132,10 @@ if (__DEV__) {
validatePropertiesInDevelopment = function(type, props) {
validateARIAProperties(type, props);
validateInputProperties(type, props);
validateUnknownProperties(type, props, /* canUseEventSystem */ true);
validateUnknownProperties(type, props, {
registrationNameModules,
possibleRegistrationNames,
});
};
// IE 11 parses & normalizes the style attribute as opposed to other

View File

@@ -17,7 +17,7 @@ import type {
SuspenseInstance,
Props,
} from './ReactDOMHostConfig';
import type {DOMTopLevelEventType} from '../legacy-events/TopLevelEventTypes';
import type {DOMTopLevelEventType} from '../events/TopLevelEventTypes';
import {
HostComponent,

View File

@@ -7,7 +7,7 @@
* @flow
*/
import type {DOMTopLevelEventType} from '../legacy-events/TopLevelEventTypes';
import type {DOMTopLevelEventType} from '../events/TopLevelEventTypes';
import type {EventPriority, ReactScopeInstance} from 'shared/ReactTypes';
import type {
ReactDOMEventHandle,

View File

@@ -7,7 +7,7 @@
* @flow
*/
import type {TopLevelType} from '../legacy-events/TopLevelEventTypes';
import type {TopLevelType} from '../events/TopLevelEventTypes';
import type {Fiber, FiberRoot} from 'react-reconciler/src/ReactInternalTypes';
import type {
BoundingRect,

View File

@@ -11,11 +11,11 @@ import type {EventPriority} from 'shared/ReactTypes';
import type {
TopLevelType,
DOMTopLevelEventType,
} from '../legacy-events/TopLevelEventTypes';
} from '../events/TopLevelEventTypes';
import type {
DispatchConfig,
CustomDispatchConfig,
} from '../legacy-events/ReactSyntheticEventType';
} from '../events/ReactSyntheticEventType';
import * as DOMTopLevelEventTypes from './DOMTopLevelEventTypes';
import {

View File

@@ -7,8 +7,8 @@
* @flow
*/
import type {AnyNativeEvent} from '../legacy-events/PluginModuleType';
import type {DOMTopLevelEventType} from '../legacy-events/TopLevelEventTypes';
import type {AnyNativeEvent} from '../events/PluginModuleType';
import type {DOMTopLevelEventType} from '../events/TopLevelEventTypes';
import type {
ElementListenerMap,
ElementListenerMapEntry,
@@ -22,14 +22,14 @@ import type {
DispatchQueueItem,
DispatchQueueItemPhase,
DispatchQueueItemPhaseEntry,
} from '../legacy-events/PluginModuleType';
} from '../events/PluginModuleType';
import type {
ReactSyntheticEvent,
CustomDispatchConfig,
} from '../legacy-events/ReactSyntheticEventType';
} from '../events/ReactSyntheticEventType';
import {registrationNameDependencies} from '../legacy-events/EventPluginRegistry';
import {plugins} from '../legacy-events/EventPluginRegistry';
import {registrationNameDependencies} from '../events/EventPluginRegistry';
import {plugins} from '../events/EventPluginRegistry';
import {
PLUGIN_EVENT_SYSTEM,
LEGACY_FB_SUPPORT,

View File

@@ -7,12 +7,12 @@
* @flow
*/
import type {DOMTopLevelEventType} from '../legacy-events/TopLevelEventTypes';
import type {DOMTopLevelEventType} from '../events/TopLevelEventTypes';
import {
unsafeCastStringToDOMTopLevelType,
unsafeCastDOMTopLevelTypeToString,
} from '../legacy-events/TopLevelEventTypes';
} from '../events/TopLevelEventTypes';
import getVendorPrefixedEventName from './getVendorPrefixedEventName';
/**

View File

@@ -12,7 +12,7 @@ import {
PASSIVE_NOT_SUPPORTED,
RESPONDER_EVENT_SYSTEM,
} from './EventSystemFlags';
import type {AnyNativeEvent} from '../legacy-events/PluginModuleType';
import type {AnyNativeEvent} from '../events/PluginModuleType';
import {
HostComponent,
ScopeComponent,
@@ -25,7 +25,7 @@ import type {
ReactDOMResponderContext,
ReactDOMResponderEvent,
} from '../shared/ReactDOMTypes';
import type {DOMTopLevelEventType} from '../legacy-events/TopLevelEventTypes';
import type {DOMTopLevelEventType} from '../events/TopLevelEventTypes';
import {
batchedEventUpdates,
discreteUpdates,

View File

@@ -0,0 +1,151 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
import type {DispatchConfig} from './ReactSyntheticEventType';
import type {
AnyNativeEvent,
LegacyPluginModule,
ModernPluginModule,
} from './PluginModuleType';
import ModernBeforeInputEventPlugin from '../events/plugins/ModernBeforeInputEventPlugin';
import ModernChangeEventPlugin from '../events/plugins/ModernChangeEventPlugin';
import ModernEnterLeaveEventPlugin from '../events/plugins/ModernEnterLeaveEventPlugin';
import ModernSelectEventPlugin from '../events/plugins/ModernSelectEventPlugin';
import ModernSimpleEventPlugin from '../events/plugins/ModernSimpleEventPlugin';
import invariant from 'shared/invariant';
/**
* Publishes an event so that it can be dispatched by the supplied plugin.
*
* @param {object} dispatchConfig Dispatch configuration for the event.
* @param {object} PluginModule Plugin publishing the event.
* @return {boolean} True if the event was successfully published.
* @private
*/
function publishEventForPlugin(
dispatchConfig: DispatchConfig,
pluginModule:
| LegacyPluginModule<AnyNativeEvent>
| ModernPluginModule<AnyNativeEvent>,
eventName: string,
): boolean {
invariant(
!eventNameDispatchConfigs.hasOwnProperty(eventName),
'EventPluginRegistry: More than one plugin attempted to publish the same ' +
'event name, `%s`.',
eventName,
);
eventNameDispatchConfigs[eventName] = dispatchConfig;
const phasedRegistrationNames = dispatchConfig.phasedRegistrationNames;
if (phasedRegistrationNames) {
for (const phaseName in phasedRegistrationNames) {
if (phasedRegistrationNames.hasOwnProperty(phaseName)) {
const phasedRegistrationName = phasedRegistrationNames[phaseName];
publishRegistrationName(
phasedRegistrationName,
pluginModule,
eventName,
);
}
}
return true;
} else if (dispatchConfig.registrationName) {
publishRegistrationName(
dispatchConfig.registrationName,
pluginModule,
eventName,
);
return true;
}
return false;
}
/**
* Publishes a registration name that is used to identify dispatched events.
*
* @param {string} registrationName Registration name to add.
* @param {object} PluginModule Plugin publishing the event.
* @private
*/
function publishRegistrationName(
registrationName: string,
pluginModule:
| LegacyPluginModule<AnyNativeEvent>
| ModernPluginModule<AnyNativeEvent>,
eventName: string,
): void {
invariant(
!registrationNameModules[registrationName],
'EventPluginRegistry: More than one plugin attempted to publish the same ' +
'registration name, `%s`.',
registrationName,
);
registrationNameModules[registrationName] = pluginModule;
registrationNameDependencies[registrationName] =
pluginModule.eventTypes[eventName].dependencies;
if (__DEV__) {
const lowerCasedName = registrationName.toLowerCase();
possibleRegistrationNames[lowerCasedName] = registrationName;
if (registrationName === 'onDoubleClick') {
possibleRegistrationNames.ondblclick = registrationName;
}
}
}
/**
* Registers plugins so that they can extract and dispatch events.
*/
/**
* Ordered list of injected plugins.
*/
export const plugins = [];
/**
* Mapping from event name to dispatch config
*/
export const eventNameDispatchConfigs = {};
/**
* Mapping from registration name to plugin module
*/
export const registrationNameModules = {};
/**
* Mapping from registration name to event name
*/
export const registrationNameDependencies = {};
/**
* Mapping from lowercase registration names to the properly cased version,
* used to warn in the case of missing event handlers. Available
* only in __DEV__.
* @type {Object}
*/
export const possibleRegistrationNames = __DEV__ ? {} : (null: any);
// Trust the developer to only use possibleRegistrationNames in __DEV__
function injectEventPlugin(pluginModule: ModernPluginModule<any>): void {
plugins.push(pluginModule);
const publishedEvents = pluginModule.eventTypes;
for (const eventName in publishedEvents) {
publishEventForPlugin(publishedEvents[eventName], pluginModule, eventName);
}
}
// TODO: remove top-level side effect.
injectEventPlugin(ModernSimpleEventPlugin);
injectEventPlugin(ModernEnterLeaveEventPlugin);
injectEventPlugin(ModernChangeEventPlugin);
injectEventPlugin(ModernSelectEventPlugin);
injectEventPlugin(ModernBeforeInputEventPlugin);

View File

@@ -7,11 +7,11 @@
* @flow
*/
import type {AnyNativeEvent} from '../legacy-events/PluginModuleType';
import type {AnyNativeEvent} from '../events/PluginModuleType';
import type {EventPriority} from 'shared/ReactTypes';
import type {FiberRoot} from 'react-reconciler/src/ReactInternalTypes';
import type {Container, SuspenseInstance} from '../client/ReactDOMHostConfig';
import type {DOMTopLevelEventType} from '../legacy-events/TopLevelEventTypes';
import type {DOMTopLevelEventType} from '../events/TopLevelEventTypes';
// Intentionally not named imports because Rollup would use dynamic dispatch for
// CommonJS interop named imports.

View File

@@ -7,9 +7,9 @@
* @flow
*/
import type {AnyNativeEvent} from '../legacy-events/PluginModuleType';
import type {AnyNativeEvent} from '../events/PluginModuleType';
import type {Container, SuspenseInstance} from '../client/ReactDOMHostConfig';
import type {DOMTopLevelEventType} from '../legacy-events/TopLevelEventTypes';
import type {DOMTopLevelEventType} from '../events/TopLevelEventTypes';
import type {ElementListenerMap} from '../client/ReactDOMComponentTree';
import type {EventSystemFlags} from './EventSystemFlags';
import type {FiberRoot} from 'react-reconciler/src/ReactInternalTypes';
@@ -35,7 +35,7 @@ import {
getClosestInstanceFromNode,
getEventListenerMap,
} from '../client/ReactDOMComponentTree';
import {unsafeCastDOMTopLevelTypeToString} from '../legacy-events/TopLevelEventTypes';
import {unsafeCastDOMTopLevelTypeToString} from '../events/TopLevelEventTypes';
import {HostRoot, SuspenseComponent} from 'react-reconciler/src/ReactWorkTags';
let attemptSynchronousHydration: (fiber: Object) => void;

View File

@@ -33,12 +33,6 @@ export type CustomDispatchConfig = {|
export type ReactSyntheticEvent = {|
dispatchConfig: DispatchConfig | CustomDispatchConfig,
getPooled: (
dispatchConfig: DispatchConfig | CustomDispatchConfig,
targetInst: Fiber,
nativeTarget: Event,
nativeEventTarget: EventTarget,
) => ReactSyntheticEvent,
isPersistent: () => boolean,
isPropagationStopped: () => boolean,
_dispatchInstances?: null | Array<Fiber | null> | Fiber,

View File

@@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import SyntheticEvent from '../legacy-events/SyntheticEvent';
import SyntheticEvent from '../events/SyntheticEvent';
/**
* @interface Event

View File

@@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import SyntheticEvent from '../legacy-events/SyntheticEvent';
import SyntheticEvent from '../events/SyntheticEvent';
/**
* @interface Event

View File

@@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import SyntheticEvent from '../legacy-events/SyntheticEvent';
import SyntheticEvent from '../events/SyntheticEvent';
/**
* @interface Event

View File

@@ -60,15 +60,6 @@ function SyntheticEvent(
nativeEvent,
nativeEventTarget,
) {
if (__DEV__) {
// these have a getter/setter for warnings
delete this.nativeEvent;
delete this.preventDefault;
delete this.stopPropagation;
delete this.isDefaultPrevented;
delete this.isPropagationStopped;
}
this.dispatchConfig = dispatchConfig;
this._targetInst = targetInst;
this.nativeEvent = nativeEvent;
@@ -78,9 +69,6 @@ function SyntheticEvent(
if (!Interface.hasOwnProperty(propName)) {
continue;
}
if (__DEV__) {
delete this[propName]; // this has a getter/setter for warnings
}
const normalize = Interface[propName];
if (normalize) {
this[propName] = normalize(nativeEvent);
@@ -157,14 +145,6 @@ Object.assign(SyntheticEvent.prototype, {
* @return {boolean} True if this should not be released, false otherwise.
*/
isPersistent: functionThatReturnsTrue,
/**
* `PooledClass` looks for `destructor` on each instance it releases.
*/
destructor: function() {
// Modern event system doesn't use pooling.
// TODO: remove calls to this.
},
});
SyntheticEvent.Interface = EventInterface;
@@ -188,34 +168,8 @@ SyntheticEvent.extend = function(Interface) {
Class.Interface = Object.assign({}, Super.Interface, Interface);
Class.extend = Super.extend;
addEventPoolingTo(Class);
return Class;
};
addEventPoolingTo(SyntheticEvent);
function createOrGetPooledEvent(
dispatchConfig,
targetInst,
nativeEvent,
nativeInst,
) {
const EventConstructor = this;
// Modern event system doesn't use pooling.
// TODO: remove this indirection.
return new EventConstructor(
dispatchConfig,
targetInst,
nativeEvent,
nativeInst,
);
}
function addEventPoolingTo(EventConstructor) {
EventConstructor.getPooled = createOrGetPooledEvent;
// Modern event system doesn't use pooling.
// TODO: remove calls to this.
}
export default SyntheticEvent;

View File

@@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import SyntheticEvent from '../legacy-events/SyntheticEvent';
import SyntheticEvent from '../events/SyntheticEvent';
/**
* @interface Event

View File

@@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import SyntheticEvent from '../legacy-events/SyntheticEvent';
import SyntheticEvent from '../events/SyntheticEvent';
/**
* @interface Event

View File

@@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import SyntheticEvent from '../legacy-events/SyntheticEvent';
import SyntheticEvent from '../events/SyntheticEvent';
const SyntheticUIEvent = SyntheticEvent.extend({
view: null,

View File

@@ -7,17 +7,6 @@
* @flow
*/
export type RNTopLevelEventType =
| 'topMouseDown'
| 'topMouseMove'
| 'topMouseUp'
| 'topScroll'
| 'topSelectionChange'
| 'topTouchCancel'
| 'topTouchEnd'
| 'topTouchMove'
| 'topTouchStart';
export opaque type DOMTopLevelEventType = string;
// Do not use the below two methods directly!
@@ -36,4 +25,4 @@ export function unsafeCastDOMTopLevelTypeToString(
return topLevelType;
}
export type TopLevelType = DOMTopLevelEventType | RNTopLevelEventType;
export type TopLevelType = DOMTopLevelEventType;

View File

@@ -12,7 +12,7 @@
* @see http://www.w3.org/TR/DOM-Level-3-Events/#keys-Modifiers
*/
import type {AnyNativeEvent} from '../legacy-events/PluginModuleType';
import type {AnyNativeEvent} from '../events/PluginModuleType';
const modifierKeyToProp = {
Alt: 'altKey',

View File

@@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import type {TopLevelType} from '../../legacy-events/TopLevelEventTypes';
import type {TopLevelType} from '../../events/TopLevelEventTypes';
import {canUseDOM} from 'shared/ExecutionEnvironment';
@@ -259,7 +259,7 @@ function extractCompositionEvent(
}
}
const event = SyntheticCompositionEvent.getPooled(
const event = new SyntheticCompositionEvent(
eventType,
null,
nativeEvent,
@@ -429,7 +429,7 @@ function extractBeforeInputEvent(
return null;
}
const event = SyntheticInputEvent.getPooled(
const event = new SyntheticInputEvent(
eventTypes.beforeInput,
null,
nativeEvent,

View File

@@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import SyntheticEvent from '../../legacy-events/SyntheticEvent';
import SyntheticEvent from '../../events/SyntheticEvent';
import isTextInputElement from '../isTextInputElement';
import {canUseDOM} from 'shared/ExecutionEnvironment';
@@ -58,7 +58,7 @@ function createAndAccumulateChangeEvent(
nativeEvent,
target,
) {
const event = SyntheticEvent.getPooled(
const event = new SyntheticEvent(
eventTypes.change,
null,
nativeEvent,

View File

@@ -145,7 +145,7 @@ const EnterLeaveEventPlugin = {
const fromNode = from == null ? win : getNodeFromInstance(from);
const toNode = to == null ? win : getNodeFromInstance(to);
const leave = eventInterface.getPooled(
const leave = new eventInterface(
leaveEventType,
from,
nativeEvent,
@@ -155,7 +155,7 @@ const EnterLeaveEventPlugin = {
leave.target = fromNode;
leave.relatedTarget = toNode;
let enter = eventInterface.getPooled(
let enter = new eventInterface(
enterEventType,
to,
nativeEvent,

View File

@@ -6,7 +6,7 @@
*/
import {canUseDOM} from 'shared/ExecutionEnvironment';
import SyntheticEvent from '../../legacy-events/SyntheticEvent';
import SyntheticEvent from '../../events/SyntheticEvent';
import isTextInputElement from '../isTextInputElement';
import shallowEqual from 'shared/shallowEqual';
@@ -132,7 +132,7 @@ function constructSelectEvent(dispatchQueue, nativeEvent, nativeEventTarget) {
if (!lastSelection || !shallowEqual(lastSelection, currentSelection)) {
lastSelection = currentSelection;
const syntheticEvent = SyntheticEvent.getPooled(
const syntheticEvent = new SyntheticEvent(
eventTypes.select,
null,
nativeEvent,

View File

@@ -10,15 +10,15 @@
import type {
TopLevelType,
DOMTopLevelEventType,
} from '../../legacy-events/TopLevelEventTypes';
} from '../../events/TopLevelEventTypes';
import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
import type {
ModernPluginModule,
DispatchQueue,
} from '../../legacy-events/PluginModuleType';
} from '../../events/PluginModuleType';
import type {EventSystemFlags} from '../EventSystemFlags';
import SyntheticEvent from '../../legacy-events/SyntheticEvent';
import SyntheticEvent from '../../events/SyntheticEvent';
import * as DOMTopLevelEventTypes from '../DOMTopLevelEventTypes';
import {
@@ -200,7 +200,7 @@ const SimpleEventPlugin: ModernPluginModule<MouseEvent> = {
EventConstructor = SyntheticEvent;
break;
}
const event = EventConstructor.getPooled(
const event = new EventConstructor(
dispatchConfig,
null,
nativeEvent,

View File

@@ -1,270 +0,0 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
import type {DispatchConfig} from './ReactSyntheticEventType';
import type {
AnyNativeEvent,
PluginName,
LegacyPluginModule,
ModernPluginModule,
} from './PluginModuleType';
import invariant from 'shared/invariant';
type NamesToPlugins = {
[key: PluginName]:
| LegacyPluginModule<AnyNativeEvent>
| ModernPluginModule<AnyNativeEvent>,
...,
};
type EventPluginOrder = null | Array<PluginName>;
/**
* Injectable ordering of event plugins.
*/
let eventPluginOrder: EventPluginOrder = null;
/**
* Injectable mapping from names to event plugin modules.
*/
const namesToPlugins: NamesToPlugins = {};
/**
* Recomputes the plugin list using the injected plugins and plugin ordering.
*
* @private
*/
function recomputePluginOrdering(): void {
if (!eventPluginOrder) {
// Wait until an `eventPluginOrder` is injected.
return;
}
for (const pluginName in namesToPlugins) {
const pluginModule = namesToPlugins[pluginName];
const pluginIndex = eventPluginOrder.indexOf(pluginName);
invariant(
pluginIndex > -1,
'EventPluginRegistry: Cannot inject event plugins that do not exist in ' +
'the plugin ordering, `%s`.',
pluginName,
);
if (plugins[pluginIndex]) {
continue;
}
invariant(
pluginModule.extractEvents,
'EventPluginRegistry: Event plugins must implement an `extractEvents` ' +
'method, but `%s` does not.',
pluginName,
);
plugins[pluginIndex] = pluginModule;
const publishedEvents = pluginModule.eventTypes;
for (const eventName in publishedEvents) {
invariant(
publishEventForPlugin(
publishedEvents[eventName],
pluginModule,
eventName,
),
'EventPluginRegistry: Failed to publish event `%s` for plugin `%s`.',
eventName,
pluginName,
);
}
}
}
/**
* Publishes an event so that it can be dispatched by the supplied plugin.
*
* @param {object} dispatchConfig Dispatch configuration for the event.
* @param {object} PluginModule Plugin publishing the event.
* @return {boolean} True if the event was successfully published.
* @private
*/
function publishEventForPlugin(
dispatchConfig: DispatchConfig,
pluginModule:
| LegacyPluginModule<AnyNativeEvent>
| ModernPluginModule<AnyNativeEvent>,
eventName: string,
): boolean {
invariant(
!eventNameDispatchConfigs.hasOwnProperty(eventName),
'EventPluginRegistry: More than one plugin attempted to publish the same ' +
'event name, `%s`.',
eventName,
);
eventNameDispatchConfigs[eventName] = dispatchConfig;
const phasedRegistrationNames = dispatchConfig.phasedRegistrationNames;
if (phasedRegistrationNames) {
for (const phaseName in phasedRegistrationNames) {
if (phasedRegistrationNames.hasOwnProperty(phaseName)) {
const phasedRegistrationName = phasedRegistrationNames[phaseName];
publishRegistrationName(
phasedRegistrationName,
pluginModule,
eventName,
);
}
}
return true;
} else if (dispatchConfig.registrationName) {
publishRegistrationName(
dispatchConfig.registrationName,
pluginModule,
eventName,
);
return true;
}
return false;
}
/**
* Publishes a registration name that is used to identify dispatched events.
*
* @param {string} registrationName Registration name to add.
* @param {object} PluginModule Plugin publishing the event.
* @private
*/
function publishRegistrationName(
registrationName: string,
pluginModule:
| LegacyPluginModule<AnyNativeEvent>
| ModernPluginModule<AnyNativeEvent>,
eventName: string,
): void {
invariant(
!registrationNameModules[registrationName],
'EventPluginRegistry: More than one plugin attempted to publish the same ' +
'registration name, `%s`.',
registrationName,
);
registrationNameModules[registrationName] = pluginModule;
registrationNameDependencies[registrationName] =
pluginModule.eventTypes[eventName].dependencies;
if (__DEV__) {
const lowerCasedName = registrationName.toLowerCase();
possibleRegistrationNames[lowerCasedName] = registrationName;
if (registrationName === 'onDoubleClick') {
possibleRegistrationNames.ondblclick = registrationName;
}
}
}
/**
* Registers plugins so that they can extract and dispatch events.
*/
/**
* Ordered list of injected plugins.
*/
export const plugins = [];
/**
* Mapping from event name to dispatch config
*/
export const eventNameDispatchConfigs = {};
/**
* Mapping from registration name to plugin module
*/
export const registrationNameModules = {};
/**
* Mapping from registration name to event name
*/
export const registrationNameDependencies = {};
/**
* Mapping from lowercase registration names to the properly cased version,
* used to warn in the case of missing event handlers. Available
* only in __DEV__.
* @type {Object}
*/
export const possibleRegistrationNames = __DEV__ ? {} : (null: any);
// Trust the developer to only use possibleRegistrationNames in __DEV__
/**
* Injects an ordering of plugins (by plugin name). This allows the ordering
* to be decoupled from injection of the actual plugins so that ordering is
* always deterministic regardless of packaging, on-the-fly injection, etc.
*
* @param {array} InjectedEventPluginOrder
* @internal
*/
export function injectEventPluginOrder(
injectedEventPluginOrder: EventPluginOrder,
): void {
invariant(
!eventPluginOrder,
'EventPluginRegistry: Cannot inject event plugin ordering more than ' +
'once. You are likely trying to load more than one copy of React.',
);
// Clone the ordering so it cannot be dynamically mutated.
eventPluginOrder = Array.prototype.slice.call(injectedEventPluginOrder);
recomputePluginOrdering();
}
/**
* Injects plugins to be used by plugin event system. The plugin names must be
* in the ordering injected by `injectEventPluginOrder`.
*
* Plugins can be injected as part of page initialization or on-the-fly.
*
* @param {object} injectedNamesToPlugins Map from names to plugin modules.
* @internal
*/
export function injectEventPluginsByName(
injectedNamesToPlugins: NamesToPlugins,
): void {
let isOrderingDirty = false;
for (const pluginName in injectedNamesToPlugins) {
if (!injectedNamesToPlugins.hasOwnProperty(pluginName)) {
continue;
}
const pluginModule = injectedNamesToPlugins[pluginName];
if (
!namesToPlugins.hasOwnProperty(pluginName) ||
namesToPlugins[pluginName] !== pluginModule
) {
invariant(
!namesToPlugins[pluginName],
'EventPluginRegistry: Cannot inject two different event plugins ' +
'using the same name, `%s`.',
pluginName,
);
namesToPlugins[pluginName] = pluginModule;
isOrderingDirty = true;
}
}
if (isOrderingDirty) {
recomputePluginOrdering();
}
}
export function injectEventPlugins(
eventPlugins: [ModernPluginModule<AnyNativeEvent>],
): void {
for (let i = 0; i < eventPlugins.length; i++) {
const pluginModule = eventPlugins[i];
plugins.push(pluginModule);
const publishedEvents = pluginModule.eventTypes;
for (const eventName in publishedEvents) {
publishEventForPlugin(
publishedEvents[eventName],
pluginModule,
eventName,
);
}
}
}

View File

@@ -1,172 +0,0 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import {invokeGuardedCallbackAndCatchFirstError} from 'shared/ReactErrorUtils';
import invariant from 'shared/invariant';
export let getFiberCurrentPropsFromNode = null;
export let getInstanceFromNode = null;
export let getNodeFromInstance = null;
export function setComponentTree(
getFiberCurrentPropsFromNodeImpl,
getInstanceFromNodeImpl,
getNodeFromInstanceImpl,
) {
getFiberCurrentPropsFromNode = getFiberCurrentPropsFromNodeImpl;
getInstanceFromNode = getInstanceFromNodeImpl;
getNodeFromInstance = getNodeFromInstanceImpl;
if (__DEV__) {
if (!getNodeFromInstance || !getInstanceFromNode) {
console.error(
'EventPluginUtils.setComponentTree(...): Injected ' +
'module is missing getNodeFromInstance or getInstanceFromNode.',
);
}
}
}
let validateEventDispatches;
if (__DEV__) {
validateEventDispatches = function(event) {
const dispatchListeners = event._dispatchListeners;
const dispatchInstances = event._dispatchInstances;
const listenersIsArr = Array.isArray(dispatchListeners);
const listenersLen = listenersIsArr
? dispatchListeners.length
: dispatchListeners
? 1
: 0;
const instancesIsArr = Array.isArray(dispatchInstances);
const instancesLen = instancesIsArr
? dispatchInstances.length
: dispatchInstances
? 1
: 0;
if (instancesIsArr !== listenersIsArr || instancesLen !== listenersLen) {
console.error('EventPluginUtils: Invalid `event`.');
}
};
}
/**
* Dispatch the event to the listener.
* @param {SyntheticEvent} event SyntheticEvent to handle
* @param {function} listener Application-level callback
* @param {*} inst Internal component instance
*/
export function executeDispatch(event, listener, inst) {
const type = event.type || 'unknown-event';
event.currentTarget = getNodeFromInstance(inst);
invokeGuardedCallbackAndCatchFirstError(type, listener, undefined, event);
event.currentTarget = null;
}
/**
* Standard/simple iteration through an event's collected dispatches.
*/
export function executeDispatchesInOrder(event) {
const dispatchListeners = event._dispatchListeners;
const dispatchInstances = event._dispatchInstances;
if (__DEV__) {
validateEventDispatches(event);
}
if (Array.isArray(dispatchListeners)) {
for (let i = 0; i < dispatchListeners.length; i++) {
if (event.isPropagationStopped()) {
break;
}
// Listeners and Instances are two parallel arrays that are always in sync.
executeDispatch(event, dispatchListeners[i], dispatchInstances[i]);
}
} else if (dispatchListeners) {
executeDispatch(event, dispatchListeners, dispatchInstances);
}
event._dispatchListeners = null;
event._dispatchInstances = null;
}
/**
* Standard/simple iteration through an event's collected dispatches, but stops
* at the first dispatch execution returning true, and returns that id.
*
* @return {?string} id of the first dispatch execution who's listener returns
* true, or null if no listener returned true.
*/
function executeDispatchesInOrderStopAtTrueImpl(event) {
const dispatchListeners = event._dispatchListeners;
const dispatchInstances = event._dispatchInstances;
if (__DEV__) {
validateEventDispatches(event);
}
if (Array.isArray(dispatchListeners)) {
for (let i = 0; i < dispatchListeners.length; i++) {
if (event.isPropagationStopped()) {
break;
}
// Listeners and Instances are two parallel arrays that are always in sync.
if (dispatchListeners[i](event, dispatchInstances[i])) {
return dispatchInstances[i];
}
}
} else if (dispatchListeners) {
if (dispatchListeners(event, dispatchInstances)) {
return dispatchInstances;
}
}
return null;
}
/**
* @see executeDispatchesInOrderStopAtTrueImpl
*/
export function executeDispatchesInOrderStopAtTrue(event) {
const ret = executeDispatchesInOrderStopAtTrueImpl(event);
event._dispatchInstances = null;
event._dispatchListeners = null;
return ret;
}
/**
* Execution of a "direct" dispatch - there must be at most one dispatch
* accumulated on the event or it is considered an error. It doesn't really make
* sense for an event with multiple dispatches (bubbled) to keep track of the
* return values at each dispatch execution, but it does tend to make sense when
* dealing with "direct" dispatches.
*
* @return {*} The return value of executing the single dispatch.
*/
export function executeDirectDispatch(event) {
if (__DEV__) {
validateEventDispatches(event);
}
const dispatchListener = event._dispatchListeners;
const dispatchInstance = event._dispatchInstances;
invariant(
!Array.isArray(dispatchListener),
'executeDirectDispatch(...): Invalid `event`.',
);
event.currentTarget = dispatchListener
? getNodeFromInstance(dispatchInstance)
: null;
const res = dispatchListener ? dispatchListener(event) : null;
event.currentTarget = null;
event._dispatchListeners = null;
event._dispatchInstances = null;
return res;
}
/**
* @param {SyntheticEvent} event
* @return {boolean} True iff number of dispatches accumulated is greater than 0.
*/
export function hasDispatches(event) {
return !!event._dispatchListeners;
}

View File

@@ -1,57 +0,0 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
import invariant from 'shared/invariant';
/**
* Accumulates items that must not be null or undefined into the first one. This
* is used to conserve memory by avoiding array allocations, and thus sacrifices
* API cleanness. Since `current` can be null before being passed in and not
* null after this function, make sure to assign it back to `current`:
*
* `a = accumulateInto(a, b);`
*
* This API should be sparingly used. Try `accumulate` for something cleaner.
*
* @return {*|array<*>} An accumulation of items.
*/
function accumulateInto<T>(
current: ?(Array<T> | T),
next: T | Array<T>,
): T | Array<T> {
invariant(
next != null,
'accumulateInto(...): Accumulated items must not be null or undefined.',
);
if (current == null) {
return next;
}
// Both are not empty. Warning: Never call x.concat(y) when you are not
// certain that x is an Array (x could be a string with concat method).
if (Array.isArray(current)) {
if (Array.isArray(next)) {
current.push.apply(current, next);
return current;
}
current.push(next);
return current;
}
if (Array.isArray(next)) {
// A bit too dangerous to mutate `next`.
return [current].concat(next);
}
return [current, next];
}
export default accumulateInto;

View File

@@ -1,31 +0,0 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
/**
* @param {array} arr an "accumulation" of items which is either an Array or
* a single item. Useful when paired with the `accumulate` module. This is a
* simple utility that allows us to reason about a collection of items, but
* handling the case when there is exactly one item (and we do not need to
* allocate an array).
* @param {function} cb Callback invoked with each element or a collection.
* @param {?} [scope] Scope used as `this` in a callback.
*/
function forEachAccumulated<T>(
arr: ?(Array<T> | T),
cb: (elem: T) => void,
scope: ?any,
) {
if (Array.isArray(arr)) {
arr.forEach(cb, scope);
} else if (arr) {
cb.call(scope, arr);
}
}
export default forEachAccumulated;

View File

@@ -114,7 +114,7 @@ if (__DEV__) {
validatePropertiesInDevelopment = function(type, props) {
validateARIAProperties(type, props);
validateInputProperties(type, props);
validateUnknownProperties(type, props, /* canUseEventSystem */ false);
validateUnknownProperties(type, props, null);
};
describeStackFrame = function(element): string {

View File

@@ -14,7 +14,7 @@ import type {
EventPriority,
ReactScopeInstance,
} from 'shared/ReactTypes';
import type {DOMTopLevelEventType} from '../legacy-events/TopLevelEventTypes';
import type {DOMTopLevelEventType} from '../events/TopLevelEventTypes';
type AnyNativeEvent = Event | KeyboardEvent | MouseEvent | Touch;

View File

@@ -5,11 +5,6 @@
* LICENSE file in the root directory of this source tree.
*/
import {
registrationNameModules,
possibleRegistrationNames,
} from '../legacy-events/EventPluginRegistry';
import {
ATTRIBUTE_NAME_CHAR,
BOOLEAN,
@@ -30,7 +25,7 @@ if (__DEV__) {
const rARIA = new RegExp('^(aria)-[' + ATTRIBUTE_NAME_CHAR + ']*$');
const rARIACamel = new RegExp('^(aria)[A-Z][' + ATTRIBUTE_NAME_CHAR + ']*$');
validateProperty = function(tagName, name, value, canUseEventSystem) {
validateProperty = function(tagName, name, value, eventRegistry) {
if (hasOwnProperty.call(warnedProperties, name) && warnedProperties[name]) {
return true;
}
@@ -47,7 +42,11 @@ if (__DEV__) {
}
// We can't rely on the event system being injected on the server.
if (canUseEventSystem) {
if (eventRegistry != null) {
const {
registrationNameModules,
possibleRegistrationNames,
} = eventRegistry;
if (registrationNameModules.hasOwnProperty(name)) {
return true;
}
@@ -240,16 +239,11 @@ if (__DEV__) {
};
}
const warnUnknownProperties = function(type, props, canUseEventSystem) {
const warnUnknownProperties = function(type, props, eventRegistry) {
if (__DEV__) {
const unknownProps = [];
for (const key in props) {
const isValid = validateProperty(
type,
key,
props[key],
canUseEventSystem,
);
const isValid = validateProperty(type, key, props[key], eventRegistry);
if (!isValid) {
unknownProps.push(key);
}
@@ -278,9 +272,9 @@ const warnUnknownProperties = function(type, props, canUseEventSystem) {
}
};
export function validateProperties(type, props, canUseEventSystem) {
export function validateProperties(type, props, eventRegistry) {
if (isCustomComponent(type, props)) {
return;
}
warnUnknownProperties(type, props, canUseEventSystem);
warnUnknownProperties(type, props, eventRegistry);
}

View File

@@ -15,12 +15,10 @@ import {
HostComponent,
HostText,
} from 'react-reconciler/src/ReactWorkTags';
import SyntheticEvent from '../legacy-events/SyntheticEvent';
import SyntheticEvent from '../events/SyntheticEvent';
import invariant from 'shared/invariant';
import {ELEMENT_NODE} from '../shared/HTMLNodeType';
import act from './ReactTestUtilsAct';
import forEachAccumulated from '../legacy-events/forEachAccumulated';
import accumulateInto from '../legacy-events/accumulateInto';
import {
rethrowCaughtError,
invokeGuardedCallbackAndCatchFirstError,
@@ -32,13 +30,12 @@ const [
/* eslint-disable no-unused-vars */
getNodeFromInstance,
getFiberCurrentPropsFromNode,
injectEventPluginsByName,
/* eslint-enable no-unused-vars */
eventNameDispatchConfigs,
enqueueStateRestore,
restoreStateIfNeeded /* eslint-disable no-unused-vars */, // TODO: remove.
,
/* dispatchEvent */ flushPassiveEffects,
restoreStateIfNeeded,
/* eslint-disable no-unused-vars */
flushPassiveEffects,
IsThisRendererActing,
/* eslint-enable no-unused-vars */
] = ReactDOM.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.Events;
@@ -386,12 +383,6 @@ function executeDispatchesInOrder(event) {
event._dispatchInstances = null;
}
/**
* Internal queue of events that have accumulated their dispatches and are
* waiting to have their dispatches executed.
*/
let eventQueue: ?(Array<ReactSyntheticEvent> | ReactSyntheticEvent) = null;
/**
* Dispatches an event and releases it back into the pool, unless persistent.
*
@@ -408,36 +399,6 @@ const executeDispatchesAndRelease = function(event: ReactSyntheticEvent) {
}
};
const executeDispatchesAndReleaseTopLevel = function(e) {
return executeDispatchesAndRelease(e);
};
function runEventsInBatch(
events: Array<ReactSyntheticEvent> | ReactSyntheticEvent | null,
) {
if (events !== null) {
eventQueue = accumulateInto(eventQueue, events);
}
// Set `eventQueue` to null before processing it so that we can tell if more
// events get enqueued while processing.
const processingEventQueue = eventQueue;
eventQueue = null;
if (!processingEventQueue) {
return;
}
forEachAccumulated(processingEventQueue, executeDispatchesAndReleaseTopLevel);
invariant(
!eventQueue,
'processEventQueue(): Additional events were enqueued while processing ' +
'an event queue. Support for this has not yet been implemented.',
);
// This would be a good time to rethrow if any of the event handlers threw.
rethrowCaughtError();
}
function isInteractive(tag) {
return (
tag === 'button' ||
@@ -541,11 +502,14 @@ function accumulateDispatches(inst, ignoredDirection, event) {
const registrationName = event.dispatchConfig.registrationName;
const listener = getListener(inst, registrationName);
if (listener) {
event._dispatchListeners = accumulateInto(
event._dispatchListeners,
listener,
);
event._dispatchInstances = accumulateInto(event._dispatchInstances, inst);
if (event._dispatchListeners == null) {
event._dispatchListeners = [];
}
if (event._dispatchInstances == null) {
event._dispatchInstances = [];
}
event._dispatchListeners.push(listener);
event._dispatchInstances.push(inst);
}
}
}
@@ -558,11 +522,14 @@ function accumulateDirectionalDispatches(inst, phase, event) {
}
const listener = listenerAtPhase(inst, event, phase);
if (listener) {
event._dispatchListeners = accumulateInto(
event._dispatchListeners,
listener,
);
event._dispatchInstances = accumulateInto(event._dispatchInstances, inst);
if (event._dispatchListeners == null) {
event._dispatchListeners = [];
}
if (event._dispatchInstances == null) {
event._dispatchInstances = [];
}
event._dispatchListeners.push(listener);
event._dispatchInstances.push(inst);
}
}
@@ -572,23 +539,15 @@ function accumulateDirectDispatchesSingle(event) {
}
}
function accumulateDirectDispatches(events) {
forEachAccumulated(events, accumulateDirectDispatchesSingle);
}
function accumulateTwoPhaseDispatchesSingle(event) {
if (event && event.dispatchConfig.phasedRegistrationNames) {
traverseTwoPhase(event._targetInst, accumulateDirectionalDispatches, event);
}
}
function accumulateTwoPhaseDispatches(events) {
forEachAccumulated(events, accumulateTwoPhaseDispatchesSingle);
}
// End of inline
const Simulate = {};
let SimulateNative;
/**
* Exports:
@@ -618,8 +577,6 @@ function makeSimulator(eventType) {
fakeNativeEvent.target = domNode;
fakeNativeEvent.type = eventType.toLowerCase();
// We don't use SyntheticEvent.getPooled in order to not have to worry about
// properly destroying any properties assigned from `eventData` upon release
const targetInst = getInstanceFromNode(domNode);
const event = new SyntheticEvent(
dispatchConfig,
@@ -634,16 +591,17 @@ function makeSimulator(eventType) {
Object.assign(event, eventData);
if (dispatchConfig.phasedRegistrationNames) {
accumulateTwoPhaseDispatches(event);
accumulateTwoPhaseDispatchesSingle(event);
} else {
accumulateDirectDispatches(event);
accumulateDirectDispatchesSingle(event);
}
ReactDOM.unstable_batchedUpdates(function() {
// Normally extractEvent enqueues a state restore, but we'll just always
// do that since we're by-passing it here.
enqueueStateRestore(domNode);
runEventsInBatch(event);
executeDispatchesAndRelease(event);
rethrowCaughtError();
});
restoreStateIfNeeded();
};
@@ -680,6 +638,5 @@ export {
mockComponent,
nativeTouchData,
Simulate,
SimulateNative,
act,
};

View File

@@ -20,11 +20,9 @@ const [
getInstanceFromNode,
getNodeFromInstance,
getFiberCurrentPropsFromNode,
injectEventPluginsByName,
eventNameDispatchConfigs,
enqueueStateRestore,
restoreStateIfNeeded,
dispatchEvent,
/* eslint-enable no-unused-vars */
flushPassiveEffects,
IsThisRendererActing,