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

Add part of the event responder system for experimental event API #15179

Merged
merged 13 commits into from
Mar 26, 2019
66 changes: 66 additions & 0 deletions packages/events/EventBatching.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/**
* 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';
import {rethrowCaughtError} from 'shared/ReactErrorUtils';

import type {ReactSyntheticEvent} from './ReactSyntheticEventType';
import accumulateInto from './accumulateInto';
import forEachAccumulated from './forEachAccumulated';
import {executeDispatchesInOrder} from './EventPluginUtils';

/**
* 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.
*
* @param {?object} event Synthetic event to be dispatched.
* @private
*/
const executeDispatchesAndRelease = function(event: ReactSyntheticEvent) {
if (event) {
executeDispatchesInOrder(event);

if (!event.isPersistent()) {
event.constructor.release(event);
}
}
};
const executeDispatchesAndReleaseTopLevel = function(e) {
return executeDispatchesAndRelease(e);
};

export 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();
}
65 changes: 5 additions & 60 deletions packages/events/EventPluginHub.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,52 +6,23 @@
* @flow
*/

import {rethrowCaughtError} from 'shared/ReactErrorUtils';
import invariant from 'shared/invariant';

import {
injectEventPluginOrder,
injectEventPluginsByName,
plugins,
} from './EventPluginRegistry';
import {
executeDispatchesInOrder,
getFiberCurrentPropsFromNode,
} from './EventPluginUtils';
import {getFiberCurrentPropsFromNode} from './EventPluginUtils';
import accumulateInto from './accumulateInto';
import forEachAccumulated from './forEachAccumulated';
import {runEventsInBatch} from './EventBatching';

import type {PluginModule} from './PluginModuleType';
import type {ReactSyntheticEvent} from './ReactSyntheticEventType';
import type {Fiber} from 'react-reconciler/src/ReactFiber';
import type {AnyNativeEvent} from './PluginModuleType';
import type {TopLevelType} from './TopLevelEventTypes';

/**
* 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.
*
* @param {?object} event Synthetic event to be dispatched.
* @private
*/
const executeDispatchesAndRelease = function(event: ReactSyntheticEvent) {
if (event) {
executeDispatchesInOrder(event);

if (!event.isPersistent()) {
event.constructor.release(event);
}
}
};
const executeDispatchesAndReleaseTopLevel = function(e) {
return executeDispatchesAndRelease(e);
};

function isInteractive(tag) {
return (
tag === 'button' ||
Expand Down Expand Up @@ -158,7 +129,7 @@ export function getListener(inst: Fiber, registrationName: string) {
* @return {*} An accumulation of synthetic events.
* @internal
*/
function extractEvents(
function extractPluginEvents(
topLevelType: TopLevelType,
targetInst: null | Fiber,
nativeEvent: AnyNativeEvent,
Expand All @@ -183,39 +154,13 @@ function extractEvents(
return events;
}

export 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();
}

export function runExtractedEventsInBatch(
export function runExtractedPluginEventsInBatch(
topLevelType: TopLevelType,
targetInst: null | Fiber,
nativeEvent: AnyNativeEvent,
nativeEventTarget: EventTarget,
) {
const events = extractEvents(
const events = extractPluginEvents(
topLevelType,
targetInst,
nativeEvent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

const {HostComponent} = require('shared/ReactWorkTags');

let EventPluginHub;
let EventBatching;
let EventPluginUtils;
let ResponderEventPlugin;

Expand Down Expand Up @@ -321,7 +321,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.runEventsInBatch(extractedEvents);
EventBatching.runEventsInBatch(extractedEvents);

// Ensure that every event that declared an `order`, was actually dispatched.
expect('number of events dispatched:' + runData.dispatchCount).toBe(
Expand Down Expand Up @@ -403,7 +403,7 @@ describe('ResponderEventPlugin', () => {
jest.resetModules();

const ReactDOMUnstableNativeDependencies = require('react-dom/unstable-native-dependencies');
EventPluginHub = require('events/EventPluginHub');
EventBatching = require('events/EventBatching');
EventPluginUtils = require('events/EventPluginUtils');
ResponderEventPlugin =
ReactDOMUnstableNativeDependencies.ResponderEventPlugin;
Expand Down
6 changes: 2 additions & 4 deletions packages/react-dom/src/client/ReactDOM.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,8 @@ import {
enqueueStateRestore,
restoreStateIfNeeded,
} from 'events/ReactControlledComponent';
import {
injection as EventPluginHubInjection,
runEventsInBatch,
} from 'events/EventPluginHub';
import {injection as EventPluginHubInjection} from 'events/EventPluginHub';
import {runEventsInBatch} from 'events/EventBatching';
import {eventNameDispatchConfigs} from 'events/EventPluginRegistry';
import {
accumulateTwoPhaseDispatches,
Expand Down
48 changes: 32 additions & 16 deletions packages/react-dom/src/client/ReactDOMHostConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,15 @@ import {
import dangerousStyleValue from '../shared/dangerousStyleValue';

import type {DOMContainer} from './ReactDOM';
import {currentEventComponentFibers} from '../events/DOMEventResponderSystem';
import type {ReactEventResponder} from 'shared/ReactTypes';
import {
REACT_EVENT_COMPONENT_TYPE,
REACT_EVENT_TARGET_TYPE,
REACT_EVENT_TARGET_TOUCH_HIT,
} from 'shared/ReactSymbols';
import getElementFromTouchHitTarget from 'shared/getElementFromTouchHitTarget';
import type {Fiber} from 'react-reconciler/src/ReactFiber';

export type Type = string;
export type Props = {
Expand Down Expand Up @@ -862,29 +864,43 @@ export function handleEventComponent(
rootContainerInstance: Container,
internalInstanceHandle: Object,
): void {
const rootElement = rootContainerInstance.ownerDocument;
listenToEventResponderEvents(eventResponder, rootElement);
if (enableEventAPI) {
// We keep the event responder hub up-to-do with the state of
// event component fiber tree (it uses this to know what events to fire).
// To do this, we update the current event component fiber and remove
// the alternate fiber if it exists.
const eventComponetFiber = ((internalInstanceHandle: any): Fiber);
currentEventComponentFibers.add(eventComponetFiber);
trueadm marked this conversation as resolved.
Show resolved Hide resolved
const alternateFiber = eventComponetFiber.alternate;
if (alternateFiber !== null) {
currentEventComponentFibers.delete(alternateFiber);
}
const rootElement = rootContainerInstance.ownerDocument;
listenToEventResponderEvents(eventResponder, rootElement);
}
}

export function handleEventTarget(
type: Symbol | number,
props: Props,
internalInstanceHandle: Object,
): void {
// Touch target hit slop handling
if (type === REACT_EVENT_TARGET_TOUCH_HIT) {
// Validates that there is a single element
const element = getElementFromTouchHitTarget(internalInstanceHandle);
if (element !== null) {
// We update the event target state node to be that of the element.
// We can then diff this entry to determine if we need to add the
// hit slop element, or change the dimensions of the hit slop.
const lastElement = internalInstanceHandle.stateNode;
if (lastElement !== element) {
internalInstanceHandle.stateNode = element;
// TODO: Create the hit slop element and attach it to the element
} else {
// TODO: Diff the left, top, right, bottom props
if (enableEventAPI) {
// Touch target hit slop handling
if (type === REACT_EVENT_TARGET_TOUCH_HIT) {
// Validates that there is a single element
const element = getElementFromTouchHitTarget(internalInstanceHandle);
if (element !== null) {
// We update the event target state node to be that of the element.
// We can then diff this entry to determine if we need to add the
// hit slop element, or change the dimensions of the hit slop.
const lastElement = internalInstanceHandle.stateNode;
if (lastElement !== element) {
internalInstanceHandle.stateNode = element;
// TODO: Create the hit slop element and attach it to the element
} else {
// TODO: Diff the left, top, right, bottom props
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/react-dom/src/events/ChangeEventPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/

import {runEventsInBatch} from 'events/EventPluginHub';
import {runEventsInBatch} from 'events/EventBatching';
import {accumulateTwoPhaseDispatches} from 'events/EventPropagators';
import {enqueueStateRestore} from 'events/ReactControlledComponent';
import {batchedUpdates} from 'events/ReactGenericBatching';
Expand Down
Loading