This commit is contained in:
Jason Quense 2018-01-08 10:36:24 -05:00
parent 26185759e4
commit 2f5d455e44
6 changed files with 79 additions and 232 deletions

View File

@ -66,6 +66,10 @@ function recomputePluginOrdering(): void {
);
plugins[pluginIndex] = pluginModule;
const publishedEvents = pluginModule.eventTypes;
if (!publishedEvents) {
return;
}
for (const eventName in publishedEvents) {
invariant(
publishEventForPlugin(
@ -145,8 +149,11 @@ function publishRegistrationName(
registrationName,
);
registrationNameModules[registrationName] = pluginModule;
registrationNameDependencies[registrationName] =
pluginModule.eventTypes[eventName].dependencies;
if (pluginModule.eventTypes) {
registrationNameDependencies[registrationName] =
pluginModule.eventTypes[eventName].dependencies;
}
if (__DEV__) {
const lowerCasedName = registrationName.toLowerCase();

View File

@ -20,7 +20,7 @@ export type AnyNativeEvent = Event | KeyboardEvent | MouseEvent | Touch;
export type PluginName = string;
export type PluginModule<NativeEvent> = {
eventTypes: EventTypes,
eventTypes?: EventTypes,
extractEvents: (
topLevelType: string,
targetInst: Fiber,

View File

@ -9,7 +9,6 @@
// TODO: direct imports like some-package/src/* are bad. Fix me.
import ReactDebugCurrentFiber from 'react-reconciler/src/ReactDebugCurrentFiber';
import {registrationNameModules} from 'events/EventPluginRegistry';
import emptyFunction from 'fbjs/lib/emptyFunction';
import warning from 'fbjs/lib/warning';
@ -312,7 +311,7 @@ function setInitialDOMProperties(
} else if (propKey === AUTOFOCUS) {
// We polyfill it separately on the client during commit.
// We blacklist it here rather than in the property list because we emit it in SSR.
} else if (registrationNameModules.hasOwnProperty(propKey)) {
} else if (propKey[0] === 'o' && propKey[1] === 'n') {
if (nextProp != null) {
if (__DEV__ && typeof nextProp !== 'function') {
warnForInvalidEventListener(propKey, nextProp);
@ -651,7 +650,7 @@ export function diffProperties(
// Noop
} else if (propKey === AUTOFOCUS) {
// Noop. It doesn't work on updates anyway.
} else if (registrationNameModules.hasOwnProperty(propKey)) {
} else if (propKey[0] === 'o' && propKey[1] === 'n') {
// 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.
@ -740,7 +739,7 @@ export function diffProperties(
propKey === SUPPRESS_HYDRATION_WARNING
) {
// Noop
} else if (registrationNameModules.hasOwnProperty(propKey)) {
} else if (propKey[0] === 'o' && propKey[1] === 'n') {
if (nextProp != null) {
// We eagerly listen to this even though we haven't committed yet.
if (__DEV__ && typeof nextProp !== 'function') {
@ -966,7 +965,7 @@ export function diffHydratedProperties(
updatePayload = [CHILDREN, '' + nextProp];
}
}
} else if (registrationNameModules.hasOwnProperty(propKey)) {
} else if (propKey[0] === 'o' && propKey[1] === 'n') {
if (nextProp != null) {
if (__DEV__ && typeof nextProp !== 'function') {
warnForInvalidEventListener(propKey, nextProp);

View File

@ -81,6 +81,15 @@ let reactTopListenersCounter = 0;
*/
const topListenersIDKey = '_reactListenersID' + ('' + Math.random()).slice(2);
function getEventsFromRegistraionName(registrationName) {
if (!(registrationName in registrationNameDependencies)) {
registrationNameDependencies[registrationName] = [
`top${registrationName.slice(2)}`,
];
}
return registrationNameDependencies[registrationName];
}
function getListeningForDocument(mountAt) {
// In IE8, `mountAt` is a host object and doesn't have `hasOwnProperty`
// directly.
@ -115,7 +124,7 @@ function getListeningForDocument(mountAt) {
export function listenTo(registrationName, contentDocumentHandle) {
const mountAt = contentDocumentHandle;
const isListening = getListeningForDocument(mountAt);
const dependencies = registrationNameDependencies[registrationName];
const dependencies = getEventsFromRegistraionName(registrationName);
for (let i = 0; i < dependencies.length; i++) {
const dependency = dependencies[i];

View File

@ -13,11 +13,10 @@ import type {
ReactSyntheticEvent,
} from 'events/ReactSyntheticEventType';
import type {Fiber} from 'react-reconciler/src/ReactFiber';
import type {EventTypes, PluginModule} from 'events/PluginModuleType';
import type {PluginModule} from 'events/PluginModuleType';
import {accumulateTwoPhaseDispatches} from 'events/EventPropagators';
import SyntheticEvent from 'events/SyntheticEvent';
import warning from 'fbjs/lib/warning';
import SyntheticAnimationEvent from './SyntheticAnimationEvent';
import SyntheticClipboardEvent from './SyntheticClipboardEvent';
@ -31,247 +30,83 @@ import SyntheticUIEvent from './SyntheticUIEvent';
import SyntheticWheelEvent from './SyntheticWheelEvent';
import getEventCharCode from './getEventCharCode';
/**
* Turns
* ['abort', ...]
* into
* eventTypes = {
* 'abort': {
* phasedRegistrationNames: {
* bubbled: 'onAbort',
* captured: 'onAbortCapture',
* },
* dependencies: ['topAbort'],
* },
* ...
* };
* topLevelEventsToDispatchConfig = {
* 'topAbort': { sameConfig }
* };
*/
const eventTypes: EventTypes = {};
const topLevelEventsToDispatchConfig: {
[key: TopLevelTypes]: DispatchConfig,
} = {};
[
'abort',
'animationEnd',
'animationIteration',
'animationStart',
'blur',
'cancel',
'canPlay',
'canPlayThrough',
'click',
'close',
'contextMenu',
'copy',
'cut',
'doubleClick',
'drag',
'dragEnd',
'dragEnter',
'dragExit',
'dragLeave',
'dragOver',
'dragStart',
'drop',
'durationChange',
'emptied',
'encrypted',
'ended',
'error',
'focus',
'input',
'invalid',
'keyDown',
'keyPress',
'keyUp',
'load',
'loadedData',
'loadedMetadata',
'loadStart',
'mouseDown',
'mouseMove',
'mouseOut',
'mouseOver',
'mouseUp',
'paste',
'pause',
'play',
'playing',
'progress',
'rateChange',
'reset',
'scroll',
'seeked',
'seeking',
'stalled',
'submit',
'suspend',
'timeUpdate',
'toggle',
'touchCancel',
'touchEnd',
'touchMove',
'touchStart',
'transitionEnd',
'volumeChange',
'waiting',
'wheel',
].forEach(event => {
const capitalizedEvent = event[0].toUpperCase() + event.slice(1);
const onEvent = 'on' + capitalizedEvent;
const topEvent = 'top' + capitalizedEvent;
const type = {
phasedRegistrationNames: {
bubbled: onEvent,
captured: onEvent + 'Capture',
},
dependencies: [topEvent],
};
eventTypes[event] = type;
topLevelEventsToDispatchConfig[topEvent] = type;
});
// Only used in DEV for exhaustiveness validation.
const knownHTMLTopLevelTypes = [
'topAbort',
'topCancel',
'topCanPlay',
'topCanPlayThrough',
'topClose',
'topDurationChange',
'topEmptied',
'topEncrypted',
'topEnded',
'topError',
'topInput',
'topInvalid',
'topLoad',
'topLoadedData',
'topLoadedMetadata',
'topLoadStart',
'topPause',
'topPlay',
'topPlaying',
'topProgress',
'topRateChange',
'topReset',
'topSeeked',
'topSeeking',
'topStalled',
'topSubmit',
'topSuspend',
'topTimeUpdate',
'topToggle',
'topVolumeChange',
'topWaiting',
];
function getDispatchConfig(topLevelType) {
if (!topLevelEventsToDispatchConfig[topLevelType]) {
const onEvent = `on${topLevelType.slice(3)}`;
topLevelEventsToDispatchConfig[topLevelType] = {
phasedRegistrationNames: {
bubbled: onEvent,
captured: `${onEvent}Capture`,
},
dependencies: [topLevelType],
};
}
return topLevelEventsToDispatchConfig[topLevelType];
}
const SimpleEventPlugin: PluginModule<MouseEvent> = {
eventTypes: eventTypes,
extractEvents: function(
extractEvents(
topLevelType: TopLevelTypes,
targetInst: Fiber,
nativeEvent: MouseEvent,
nativeEventTarget: EventTarget,
): null | ReactSyntheticEvent {
const dispatchConfig = topLevelEventsToDispatchConfig[topLevelType];
if (!dispatchConfig) {
return null;
}
let EventConstructor;
switch (topLevelType) {
case 'topKeyPress':
// Firefox creates a keypress event for function keys too. This removes
// the unwanted keypress events. Enter is however both printable and
// non-printable. One would expect Tab to be as well (but it isn't).
if (getEventCharCode(nativeEvent) === 0) {
return null;
}
/* falls through */
case 'topKeyDown':
case 'topKeyUp':
EventConstructor = SyntheticKeyboardEvent;
break;
case 'topBlur':
case 'topFocus':
EventConstructor = SyntheticFocusEvent;
break;
case 'topClick':
// Firefox creates a click event on right mouse clicks. This removes the
// unwanted click events.
if (nativeEvent.button === 2) {
return null;
}
/* falls through */
case 'topDoubleClick':
case 'topMouseDown':
case 'topMouseMove':
case 'topMouseUp':
// TODO: Disabled elements should not respond to mouse events
/* falls through */
case 'topMouseOut':
case 'topMouseOver':
case 'topContextMenu':
let nativeCtor = nativeEvent.constructor.name;
switch (nativeCtor) {
case 'MouseEvent':
EventConstructor = SyntheticMouseEvent;
break;
case 'topDrag':
case 'topDragEnd':
case 'topDragEnter':
case 'topDragExit':
case 'topDragLeave':
case 'topDragOver':
case 'topDragStart':
case 'topDrop':
EventConstructor = SyntheticDragEvent;
case 'FocusEvent':
EventConstructor = SyntheticFocusEvent;
break;
case 'topTouchCancel':
case 'topTouchEnd':
case 'topTouchMove':
case 'topTouchStart':
EventConstructor = SyntheticTouchEvent;
case 'KeyboardEvent':
EventConstructor = SyntheticKeyboardEvent;
break;
case 'topAnimationEnd':
case 'topAnimationIteration':
case 'topAnimationStart':
EventConstructor = SyntheticAnimationEvent;
break;
case 'topTransitionEnd':
case 'TransitionEvent':
EventConstructor = SyntheticTransitionEvent;
break;
case 'topScroll':
case 'AnimationEvent':
EventConstructor = SyntheticAnimationEvent;
break;
case 'DragEvent':
EventConstructor = SyntheticDragEvent;
break;
case 'UIEvent':
EventConstructor = SyntheticUIEvent;
break;
case 'topWheel':
EventConstructor = SyntheticWheelEvent;
break;
case 'topCopy':
case 'topCut':
case 'topPaste':
case 'ClipboardEvent':
EventConstructor = SyntheticClipboardEvent;
break;
default:
if (__DEV__) {
if (knownHTMLTopLevelTypes.indexOf(topLevelType) === -1) {
warning(
false,
'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
EventConstructor = SyntheticEvent;
case 'TouchEvent':
EventConstructor = SyntheticTouchEvent;
break;
case 'WheelEvent':
EventConstructor = SyntheticWheelEvent;
break;
default:
EventConstructor = SyntheticEvent;
}
// Firefox creates a keypress event for function keys too. This removes
// the unwanted keypress events. Enter is however both printable and
// non-printable. One would expect Tab to be as well (but it isn't).
if (topLevelType === 'topKeyPress' && getEventCharCode(nativeEvent) === 0) {
return null;
}
// Firefox creates a click event on right mouse clicks. This removes the
// unwanted click events.
if (topLevelType === 'topKeyPress' && nativeEvent.button === 2) {
return null;
}
const event = EventConstructor.getPooled(
dispatchConfig,
getDispatchConfig(topLevelType),
targetInst,
nativeEvent,
nativeEventTarget,

View File

@ -5,10 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import {
registrationNameModules,
possibleRegistrationNames,
} from 'events/EventPluginRegistry';
import {possibleRegistrationNames} from 'events/EventPluginRegistry';
import {ReactDebugCurrentFrame} from 'shared/ReactGlobalSharedState';
import warning from 'fbjs/lib/warning';
@ -55,7 +52,7 @@ if (__DEV__) {
// We can't rely on the event system being injected on the server.
if (canUseEventSystem) {
if (registrationNameModules.hasOwnProperty(name)) {
if (name[0] === 'o' && name[1] === 'n') {
return true;
}
const registrationName = possibleRegistrationNames.hasOwnProperty(