Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make event plugin injection statically resolvable #19234

Merged
merged 7 commits into from
Jul 1, 2020
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 6 additions & 6 deletions packages/react-dom/src/client/ReactDOMComponent.js
Expand Up @@ -8,7 +8,7 @@
*/

import {
registrationNameModules,
registrationNames,
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This used to be an object hash of name -> plugin. But the plugin object wasn't actually used. I changed it to a hash of name -> true. This lets us get rid of object "plugin" representations.

possibleRegistrationNames,
} from '../events/EventPluginRegistry';
import {canUseDOM} from 'shared/ExecutionEnvironment';
Expand Down Expand Up @@ -133,7 +133,7 @@ if (__DEV__) {
validateARIAProperties(type, props);
validateInputProperties(type, props);
validateUnknownProperties(type, props, {
registrationNameModules,
registrationNames,
possibleRegistrationNames,
});
};
Expand Down Expand Up @@ -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 (registrationNameModules.hasOwnProperty(propKey)) {
} else if (registrationNames.hasOwnProperty(propKey)) {
if (nextProp != null) {
if (__DEV__ && typeof nextProp !== 'function') {
warnForInvalidEventListener(propKey, nextProp);
Expand Down Expand Up @@ -694,7 +694,7 @@ export function diffProperties(
// Noop
} else if (propKey === AUTOFOCUS) {
// Noop. It doesn't work on updates anyway.
} else if (registrationNameModules.hasOwnProperty(propKey)) {
} else if (registrationNames.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.
Expand Down Expand Up @@ -781,7 +781,7 @@ export function diffProperties(
propKey === SUPPRESS_HYDRATION_WARNING
) {
// Noop
} else if (registrationNameModules.hasOwnProperty(propKey)) {
} else if (registrationNames.hasOwnProperty(propKey)) {
if (nextProp != null) {
// We eagerly listen to this even though we haven't committed yet.
if (__DEV__ && typeof nextProp !== 'function') {
Expand Down Expand Up @@ -978,7 +978,7 @@ export function diffHydratedProperties(
updatePayload = [CHILDREN, '' + nextProp];
}
}
} else if (registrationNameModules.hasOwnProperty(propKey)) {
} else if (registrationNames.hasOwnProperty(propKey)) {
if (nextProp != null) {
if (__DEV__ && typeof nextProp !== 'function') {
warnForInvalidEventListener(propKey, nextProp);
Expand Down
3 changes: 2 additions & 1 deletion packages/react-dom/src/events/DOMEventProperties.js
Expand Up @@ -12,6 +12,7 @@ import type {
TopLevelType,
DOMTopLevelEventType,
} from '../events/TopLevelEventTypes';
import type {EventTypes} from '../events/PluginModuleType';
import type {
DispatchConfig,
CustomDispatchConfig,
Expand All @@ -32,7 +33,7 @@ import {enableCreateEventHandleAPI} from 'shared/ReactFeatureFlags';
// here once. If we remove or refactor the
// SimpleEventPlugin, we should also remove or
// update the below line.
export const simpleEventPluginEventTypes = {};
export const simpleEventPluginEventTypes: EventTypes = {};

export const topLevelEventsToDispatchConfig: Map<
TopLevelType,
Expand Down
30 changes: 13 additions & 17 deletions packages/react-dom/src/events/DOMModernPluginEventSystem.js
Expand Up @@ -17,7 +17,6 @@ import type {EventSystemFlags} from './EventSystemFlags';
import type {EventPriority, ReactScopeInstance} from 'shared/ReactTypes';
import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
import type {
ModernPluginModule,
DispatchQueue,
DispatchQueueItem,
DispatchQueueItemPhase,
Expand All @@ -28,8 +27,10 @@ import type {
CustomDispatchConfig,
} from '../events/ReactSyntheticEventType';

import {registrationNameDependencies} from '../events/EventPluginRegistry';
import {plugins} from '../events/EventPluginRegistry';
import {
extractEvents,
registrationNameDependencies,
} from '../events/EventPluginRegistry';
import {
PLUGIN_EVENT_SYSTEM,
LEGACY_FB_SUPPORT,
Expand Down Expand Up @@ -218,22 +219,17 @@ function dispatchEventsForPlugins(
targetInst: null | Fiber,
targetContainer: EventTarget,
): void {
const modernPlugins = ((plugins: any): Array<ModernPluginModule<Event>>);
const nativeEventTarget = getEventTarget(nativeEvent);
const dispatchQueue: DispatchQueue = [];

for (let i = 0; i < modernPlugins.length; i++) {
const plugin = modernPlugins[i];
plugin.extractEvents(
dispatchQueue,
topLevelType,
targetInst,
nativeEvent,
nativeEventTarget,
eventSystemFlags,
targetContainer,
);
}
extractEvents(
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This calls into a function that will statically enumerate each of them.

dispatchQueue,
topLevelType,
targetInst,
nativeEvent,
nativeEventTarget,
eventSystemFlags,
targetContainer,
);
dispatchEventsInBatch(dispatchQueue);
}

Expand Down
146 changes: 84 additions & 62 deletions packages/react-dom/src/events/EventPluginRegistry.js
Expand Up @@ -7,33 +7,22 @@
* @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 type {TopLevelType} from './TopLevelEventTypes';
import type {DispatchQueue} from './PluginModuleType';
import type {EventSystemFlags} from './EventSystemFlags';
import type {AnyNativeEvent, EventTypes} from './PluginModuleType';
import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';

import * as ModernBeforeInputEventPlugin from '../events/plugins/ModernBeforeInputEventPlugin';
import * as ModernChangeEventPlugin from '../events/plugins/ModernChangeEventPlugin';
import * as ModernEnterLeaveEventPlugin from '../events/plugins/ModernEnterLeaveEventPlugin';
import * as ModernSelectEventPlugin from '../events/plugins/ModernSelectEventPlugin';
import * as 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>,
eventTypes: EventTypes,
eventName: string,
): boolean {
invariant(
Expand All @@ -42,55 +31,43 @@ function publishEventForPlugin(
'event name, `%s`.',
eventName,
);
const dispatchConfig = eventTypes[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,
);
publishRegistrationName(phasedRegistrationName, eventTypes, eventName);
}
}
return true;
} else if (dispatchConfig.registrationName) {
publishRegistrationName(
dispatchConfig.registrationName,
pluginModule,
eventTypes,
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>,
eventTypes: EventTypes,
eventName: string,
): void {
invariant(
!registrationNameModules[registrationName],
!registrationNames[registrationName],
'EventPluginRegistry: More than one plugin attempted to publish the same ' +
'registration name, `%s`.',
registrationName,
);
registrationNameModules[registrationName] = pluginModule;
registrationNames[registrationName] = true;
registrationNameDependencies[registrationName] =
pluginModule.eventTypes[eventName].dependencies;
eventTypes[eventName].dependencies;

if (__DEV__) {
const lowerCasedName = registrationName.toLowerCase();
Expand All @@ -102,15 +79,6 @@ function publishRegistrationName(
}
}

/**
* 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
*/
Expand All @@ -119,7 +87,7 @@ export const eventNameDispatchConfigs = {};
/**
* Mapping from registration name to plugin module
*/
export const registrationNameModules = {};
export const registrationNames = {};

/**
* Mapping from registration name to event name
Expand All @@ -135,17 +103,71 @@ export const registrationNameDependencies = {};
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);
function injectEventPlugin(eventTypes: EventTypes): void {
for (const eventName in eventTypes) {
publishEventForPlugin(eventTypes, eventName);
}
}

export function extractEvents(
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the new static thing.

dispatchQueue: DispatchQueue,
topLevelType: TopLevelType,
targetInst: null | Fiber,
nativeEvent: AnyNativeEvent,
nativeEventTarget: null | EventTarget,
eventSystemFlags: EventSystemFlags,
targetContainer: null | EventTarget,
) {
ModernSimpleEventPlugin.extractEvents(
dispatchQueue,
topLevelType,
targetInst,
nativeEvent,
nativeEventTarget,
eventSystemFlags,
targetContainer,
);
ModernEnterLeaveEventPlugin.extractEvents(
dispatchQueue,
topLevelType,
targetInst,
nativeEvent,
nativeEventTarget,
eventSystemFlags,
targetContainer,
);
ModernChangeEventPlugin.extractEvents(
dispatchQueue,
topLevelType,
targetInst,
nativeEvent,
nativeEventTarget,
eventSystemFlags,
targetContainer,
);
ModernSelectEventPlugin.extractEvents(
dispatchQueue,
topLevelType,
targetInst,
nativeEvent,
nativeEventTarget,
eventSystemFlags,
targetContainer,
);
ModernBeforeInputEventPlugin.extractEvents(
dispatchQueue,
topLevelType,
targetInst,
nativeEvent,
nativeEventTarget,
eventSystemFlags,
targetContainer,
);
}

// TODO: remove top-level side effect.
injectEventPlugin(ModernSimpleEventPlugin);
injectEventPlugin(ModernEnterLeaveEventPlugin);
injectEventPlugin(ModernChangeEventPlugin);
injectEventPlugin(ModernSelectEventPlugin);
injectEventPlugin(ModernBeforeInputEventPlugin);
injectEventPlugin(ModernSimpleEventPlugin.eventTypes);
injectEventPlugin(ModernEnterLeaveEventPlugin.eventTypes);
injectEventPlugin(ModernChangeEventPlugin.eventTypes);
injectEventPlugin(ModernSelectEventPlugin.eventTypes);
injectEventPlugin(ModernBeforeInputEventPlugin.eventTypes);
27 changes: 0 additions & 27 deletions packages/react-dom/src/events/PluginModuleType.js
Expand Up @@ -12,7 +12,6 @@ import type {
DispatchConfig,
ReactSyntheticEvent,
} from './ReactSyntheticEventType';
import type {TopLevelType} from './TopLevelEventTypes';

export type EventTypes = {[key: string]: DispatchConfig, ...};

Expand All @@ -22,19 +21,6 @@ export type PluginName = string;

export type EventSystemFlags = number;

export type LegacyPluginModule<NativeEvent> = {
eventTypes: EventTypes,
extractEvents: (
topLevelType: TopLevelType,
targetInst: null | Fiber,
nativeTarget: NativeEvent,
nativeEventTarget: null | EventTarget,
eventSystemFlags?: number,
container?: null | EventTarget,
) => ?ReactSyntheticEvent,
tapMoveThreshold?: number,
};

export type DispatchQueueItemPhaseEntry = {|
instance: null | Fiber,
listener: Function,
Expand All @@ -50,16 +36,3 @@ export type DispatchQueueItem = {|
|};

export type DispatchQueue = Array<DispatchQueueItem>;

export type ModernPluginModule<NativeEvent> = {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This concept sort of goes away.

eventTypes: EventTypes,
extractEvents: (
dispatchQueue: DispatchQueue,
topLevelType: TopLevelType,
targetInst: null | Fiber,
nativeTarget: NativeEvent,
nativeEventTarget: null | EventTarget,
eventSystemFlags: number,
container: null | EventTarget,
) => void,
};