Reduce the handleTopLevel() event code indirection (#11915)

* Refactor event emitters to reduce indirection

* Remove unused handleTopLevel() injection

* Rename handleTopLevel() to runExtractedEventsInBatch() and remove import indirection
This commit is contained in:
Dan Abramov
2018-01-05 18:37:13 +00:00
committed by GitHub
parent 8b496ba28f
commit f265656382
9 changed files with 42 additions and 86 deletions
+22 -20
View File
@@ -164,13 +164,13 @@ export function getListener(inst: Fiber, registrationName: string) {
* @return {*} An accumulation of synthetic events.
* @internal
*/
export function extractEvents(
function extractEvents(
topLevelType: string,
targetInst: Fiber,
nativeEvent: AnyNativeEvent,
nativeEventTarget: EventTarget,
) {
let events;
): Array<ReactSyntheticEvent> | ReactSyntheticEvent | null {
let events = null;
for (let i = 0; i < plugins.length; i++) {
// Not every plugin in the ordering may be loaded at runtime.
const possiblePlugin: PluginModule<AnyNativeEvent> = plugins[i];
@@ -189,27 +189,14 @@ export function extractEvents(
return events;
}
/**
* Enqueues a synthetic event that should be dispatched when
* `processEventQueue` is invoked.
*
* @param {*} events An accumulation of synthetic events.
* @internal
*/
export function enqueueEvents(
events: Array<ReactSyntheticEvent> | ReactSyntheticEvent,
export function runEventsInBatch(
events: Array<ReactSyntheticEvent> | ReactSyntheticEvent | null,
simulated: boolean,
) {
if (events) {
if (events !== null) {
eventQueue = accumulateInto(eventQueue, events);
}
}
/**
* Dispatches all synthetic events on the event queue.
*
* @internal
*/
export function processEventQueue(simulated: boolean) {
// Set `eventQueue` to null before processing it so that we can tell if more
// events get enqueued while processing.
const processingEventQueue = eventQueue;
@@ -238,3 +225,18 @@ export function processEventQueue(simulated: boolean) {
// This would be a good time to rethrow if any of the event handlers threw.
ReactErrorUtils.rethrowCaughtError();
}
export function runExtractedEventsInBatch(
topLevelType: string,
targetInst: Fiber,
nativeEvent: AnyNativeEvent,
nativeEventTarget: EventTarget,
) {
const events = extractEvents(
topLevelType,
targetInst,
nativeEvent,
nativeEventTarget,
);
runEventsInBatch(events, false);
}
-36
View File
@@ -1,36 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import {
enqueueEvents,
processEventQueue,
extractEvents,
} from './EventPluginHub';
function runEventQueueInBatch(events) {
enqueueEvents(events);
processEventQueue(false);
}
/**
* Streams a fired top-level event to `EventPluginHub` where plugins have the
* opportunity to create `ReactEvent`s to be dispatched.
*/
export function handleTopLevel(
topLevelType,
targetInst,
nativeEvent,
nativeEventTarget,
) {
const events = extractEvents(
topLevelType,
targetInst,
nativeEvent,
nativeEventTarget,
);
runEventQueueInBatch(events);
}
@@ -312,8 +312,7 @@ const run = function(config, hierarchyConfig, nativeEventConfig) {
// At this point the negotiation events have been dispatched as part of the
// extraction process, but not the side effectful events. Below, we dispatch
// side effectful events.
EventPluginHub.enqueueEvents(extractedEvents);
EventPluginHub.processEventQueue(true);
EventPluginHub.runEventsInBatch(extractedEvents, true);
// Ensure that every event that declared an `order`, was actually dispatched.
expect('number of events dispatched:' + runData.dispatchCount).toBe(
@@ -13,13 +13,9 @@ import BeforeInputEventPlugin from '../events/BeforeInputEventPlugin';
import ChangeEventPlugin from '../events/ChangeEventPlugin';
import DOMEventPluginOrder from '../events/DOMEventPluginOrder';
import EnterLeaveEventPlugin from '../events/EnterLeaveEventPlugin';
import {handleTopLevel} from '../events/ReactBrowserEventEmitter';
import {setHandleTopLevel} from '../events/ReactDOMEventListener';
import SelectEventPlugin from '../events/SelectEventPlugin';
import SimpleEventPlugin from '../events/SimpleEventPlugin';
setHandleTopLevel(handleTopLevel);
/**
* Inject modules for resolving DOM hierarchy and plugin ordering.
*/
+2 -3
View File
@@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import {enqueueEvents, processEventQueue} from 'events/EventPluginHub';
import * as EventPluginHub from 'events/EventPluginHub';
import {accumulateTwoPhaseDispatches} from 'events/EventPropagators';
import {enqueueStateRestore} from 'events/ReactControlledComponent';
import {batchedUpdates} from 'events/ReactGenericBatching';
@@ -89,8 +89,7 @@ function manualDispatchChangeEvent(nativeEvent) {
}
function runEventInBatch(event) {
enqueueEvents(event);
processEventQueue(false);
EventPluginHub.runEventsInBatch(event, false);
}
function getInstIfValueChanged(targetInst) {
@@ -6,7 +6,6 @@
*/
import {registrationNameDependencies} from 'events/EventPluginRegistry';
import {
setEnabled,
isEnabled,
@@ -16,8 +15,6 @@ import {
import isEventSupported from './isEventSupported';
import BrowserEventConstants from './BrowserEventConstants';
export * from 'events/ReactEventEmitterMixin';
const {topLevelTypes} = BrowserEventConstants;
/**
+4 -8
View File
@@ -6,6 +6,7 @@
*/
import {batchedUpdates} from 'events/ReactGenericBatching';
import {runExtractedEventsInBatch} from 'events/EventPluginHub';
import {isFiberMounted} from 'react-reconciler/reflection';
import {HostRoot} from 'shared/ReactTypeOfWork';
@@ -62,7 +63,7 @@ function releaseTopLevelCallbackBookKeeping(instance) {
}
}
function handleTopLevelImpl(bookKeeping) {
function handleTopLevel(bookKeeping) {
let targetInst = bookKeeping.targetInst;
// Loop through the hierarchy, in case there's any nested components.
@@ -85,7 +86,7 @@ function handleTopLevelImpl(bookKeeping) {
for (let i = 0; i < bookKeeping.ancestors.length; i++) {
targetInst = bookKeeping.ancestors[i];
_handleTopLevel(
runExtractedEventsInBatch(
bookKeeping.topLevelType,
targetInst,
bookKeeping.nativeEvent,
@@ -96,11 +97,6 @@ function handleTopLevelImpl(bookKeeping) {
// TODO: can we stop exporting these?
export let _enabled = true;
export let _handleTopLevel: null;
export function setHandleTopLevel(handleTopLevel) {
_handleTopLevel = handleTopLevel;
}
export function setEnabled(enabled) {
_enabled = !!enabled;
@@ -180,7 +176,7 @@ export function dispatchEvent(topLevelType, nativeEvent) {
try {
// Event queue being processed in the same cycle allows
// `preventDefault`.
batchedUpdates(handleTopLevelImpl, bookKeeping);
batchedUpdates(handleTopLevel, bookKeeping);
} finally {
releaseTopLevelCallbackBookKeeping(bookKeeping);
}
+1 -3
View File
@@ -388,9 +388,7 @@ function makeSimulator(eventType) {
// Normally extractEvent enqueues a state restore, but we'll just always
// do that since we we're by-passing it here.
ReactControlledComponent.enqueueStateRestore(domNode);
EventPluginHub.enqueueEvents(event);
EventPluginHub.processEventQueue(true);
EventPluginHub.runEventsInBatch(event, true);
});
};
}
@@ -7,16 +7,16 @@
* @flow
*/
import {getListener} from 'events/EventPluginHub';
import {getListener, runExtractedEventsInBatch} from 'events/EventPluginHub';
import {registrationNameModules} from 'events/EventPluginRegistry';
import {batchedUpdates} from 'events/ReactGenericBatching';
import {handleTopLevel} from 'events/ReactEventEmitterMixin';
import warning from 'fbjs/lib/warning';
import {getInstanceFromNode} from './ReactNativeComponentTree';
import ReactNativeTagHandles from './ReactNativeTagHandles';
export * from 'events/ReactEventEmitterMixin';
import type {AnyNativeEvent} from 'events/PluginModuleType';
export {getListener, registrationNameModules as registrationNames};
/**
@@ -25,7 +25,7 @@ export {getListener, registrationNameModules as registrationNames};
*/
// Shared default empty native event - conserve memory.
const EMPTY_NATIVE_EVENT = {};
const EMPTY_NATIVE_EVENT = (({}: any): AnyNativeEvent);
/**
* Selects a subsequence of `Touch`es, without destroying `touches`.
@@ -90,12 +90,17 @@ const removeTouchesAtIndices = function(
export function _receiveRootNodeIDEvent(
rootNodeID: number,
topLevelType: string,
nativeEventParam: ?Object,
nativeEventParam: ?AnyNativeEvent,
) {
const nativeEvent = nativeEventParam || EMPTY_NATIVE_EVENT;
const inst = getInstanceFromNode(rootNodeID);
batchedUpdates(function() {
handleTopLevel(topLevelType, inst, nativeEvent, nativeEvent.target);
runExtractedEventsInBatch(
topLevelType,
inst,
nativeEvent,
nativeEvent.target,
);
});
// React Native doesn't use ReactControlledComponent but if it did, here's
// where it would do it.
@@ -111,7 +116,7 @@ export function _receiveRootNodeIDEvent(
export function receiveEvent(
rootNodeID: number,
topLevelType: string,
nativeEventParam: Object,
nativeEventParam: AnyNativeEvent,
) {
_receiveRootNodeIDEvent(rootNodeID, topLevelType, nativeEventParam);
}