Skip to content

Commit

Permalink
Experimental event API: rework the propagation system for event compo…
Browse files Browse the repository at this point in the history
…nents (#15462)
  • Loading branch information
trueadm authored Apr 23, 2019
1 parent 5876769 commit 784ebd8
Show file tree
Hide file tree
Showing 9 changed files with 342 additions and 434 deletions.
112 changes: 48 additions & 64 deletions packages/react-dom/src/events/DOMEventResponderSystem.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
HostComponent,
} from 'shared/ReactWorkTags';
import type {
ReactEventResponder,
ReactEventResponderEventType,
ReactEventComponentInstance,
ReactResponderContext,
Expand Down Expand Up @@ -61,13 +62,10 @@ type ResponderTimeout = {|

type ResponderTimer = {|
instance: ReactEventComponentInstance,
func: () => boolean,
func: () => void,
id: Symbol,
|};

const ROOT_PHASE = 0;
const BUBBLE_PHASE = 1;
const CAPTURE_PHASE = 2;
const activeTimeouts: Map<Symbol, ResponderTimeout> = new Map();
const rootEventTypesToEventComponentInstances: Map<
DOMTopLevelEventType | string,
Expand Down Expand Up @@ -253,7 +251,7 @@ const eventResponderContext: ReactResponderContext = {
triggerOwnershipListeners();
return false;
},
setTimeout(func: () => boolean, delay): Symbol {
setTimeout(func: () => void, delay): Symbol {
validateResponderContext();
if (currentTimers === null) {
currentTimers = new Map();
Expand Down Expand Up @@ -349,16 +347,13 @@ const eventResponderContext: ReactResponderContext = {

function processTimers(timers: Map<Symbol, ResponderTimer>): void {
const timersArr = Array.from(timers.values());
let shouldStopPropagation = false;
currentEventQueue = createEventQueue();
try {
for (let i = 0; i < timersArr.length; i++) {
const {instance, func, id} = timersArr[i];
currentInstance = instance;
try {
if (!shouldStopPropagation) {
shouldStopPropagation = func();
}
func();
} finally {
activeTimeouts.delete(id);
}
Expand Down Expand Up @@ -390,15 +385,13 @@ function createResponderEvent(
nativeEvent: AnyNativeEvent,
nativeEventTarget: Element | Document,
eventSystemFlags: EventSystemFlags,
phase: 0 | 1 | 2,
): ReactResponderEvent {
const responderEvent = {
nativeEvent: nativeEvent,
target: nativeEventTarget,
type: topLevelType,
passive: (eventSystemFlags & IS_PASSIVE) !== 0,
passiveSupported: (eventSystemFlags & PASSIVE_NOT_SUPPORTED) === 0,
phase,
};
if (__DEV__) {
Object.freeze(responderEvent);
Expand Down Expand Up @@ -510,16 +503,7 @@ function getRootEventResponderInstances(
return eventResponderInstances;
}

function triggerEventResponderEventListener(
responderEvent: ReactResponderEvent,
eventComponentInstance: ReactEventComponentInstance,
): boolean {
const {responder, props, state} = eventComponentInstance;
currentInstance = eventComponentInstance;
return responder.onEvent(responderEvent, eventResponderContext, props, state);
}

function traverseAndTriggerEventResponderInstances(
function traverseAndHandleEventResponderInstances(
topLevelType: DOMTopLevelEventType,
targetFiber: null | Fiber,
nativeEvent: AnyNativeEvent,
Expand All @@ -535,46 +519,54 @@ function traverseAndTriggerEventResponderInstances(
topLevelType,
targetFiber,
);
const responderEvent = createResponderEvent(
((topLevelType: any): string),
nativeEvent,
((nativeEventTarget: any): Element | Document),
eventSystemFlags,
);
const propagatedEventResponders: Set<ReactEventResponder> = new Set();
let length = targetEventResponderInstances.length;
let i;
let shouldStopPropagation = false;
let responderEvent;

// Captured and bubbled event phases have the notion of local propagation.
// This means that the propgation chain can be stopped part of the the way
// through processing event component instances. The major difference to other
// events systems is that the stopping of propgation is localized to a single
// phase, rather than both phases.
if (length > 0) {
// Capture target phase
responderEvent = createResponderEvent(
((topLevelType: any): string),
nativeEvent,
((nativeEventTarget: any): Element | Document),
eventSystemFlags,
CAPTURE_PHASE,
);
for (i = length; i-- > 0; ) {
const targetEventResponderInstance = targetEventResponderInstances[i];
shouldStopPropagation = triggerEventResponderEventListener(
responderEvent,
targetEventResponderInstance,
);
if (shouldStopPropagation) {
return;
const {responder, props, state} = targetEventResponderInstance;
if (responder.stopLocalPropagation) {
if (propagatedEventResponders.has(responder)) {
continue;
}
propagatedEventResponders.add(responder);
}
const eventListener = responder.onEventCapture;
if (eventListener !== undefined) {
currentInstance = targetEventResponderInstance;
eventListener(responderEvent, eventResponderContext, props, state);
}
}
// We clean propagated event responders between phases.
propagatedEventResponders.clear();
// Bubble target phase
responderEvent = createResponderEvent(
((topLevelType: any): string),
nativeEvent,
((nativeEventTarget: any): Element | Document),
eventSystemFlags,
BUBBLE_PHASE,
);
for (i = 0; i < length; i++) {
const targetEventResponderInstance = targetEventResponderInstances[i];
shouldStopPropagation = triggerEventResponderEventListener(
responderEvent,
targetEventResponderInstance,
);
if (shouldStopPropagation) {
return;
const {responder, props, state} = targetEventResponderInstance;
if (responder.stopLocalPropagation) {
if (propagatedEventResponders.has(responder)) {
continue;
}
propagatedEventResponders.add(responder);
}
const eventListener = responder.onEvent;
if (eventListener !== undefined) {
currentInstance = targetEventResponderInstance;
eventListener(responderEvent, eventResponderContext, props, state);
}
}
}
Expand All @@ -584,21 +576,13 @@ function traverseAndTriggerEventResponderInstances(
);
length = rootEventResponderInstances.length;
if (length > 0) {
responderEvent = createResponderEvent(
((topLevelType: any): string),
nativeEvent,
((nativeEventTarget: any): Element | Document),
eventSystemFlags,
ROOT_PHASE,
);
for (i = 0; i < length; i++) {
const targetEventResponderInstance = rootEventResponderInstances[i];
shouldStopPropagation = triggerEventResponderEventListener(
responderEvent,
targetEventResponderInstance,
);
if (shouldStopPropagation) {
return;
const rootEventResponderInstance = rootEventResponderInstances[i];
const {responder, props, state} = rootEventResponderInstance;
const eventListener = responder.onRootEvent;
if (eventListener !== undefined) {
currentInstance = rootEventResponderInstance;
eventListener(responderEvent, eventResponderContext, props, state);
}
}
}
Expand Down Expand Up @@ -672,7 +656,7 @@ export function dispatchEventForResponderEventSystem(
if (enableEventAPI) {
currentEventQueue = createEventQueue();
try {
traverseAndTriggerEventResponderInstances(
traverseAndHandleEventResponderInstances(
topLevelType,
targetFiber,
nativeEvent,
Expand Down
Loading

0 comments on commit 784ebd8

Please sign in to comment.