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

[Flare] Revise responder event types #16081

Merged
merged 2 commits into from
Jul 8, 2019
Merged
Show file tree
Hide file tree
Changes from all 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
6 changes: 3 additions & 3 deletions packages/react-art/src/ReactARTHostConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -428,19 +428,19 @@ export function unhideTextInstance(textInstance, text): void {
}

export function mountEventComponent(
eventComponentInstance: ReactEventComponentInstance<any, any, any>,
eventComponentInstance: ReactEventComponentInstance<any, any>,
) {
throw new Error('Not yet implemented.');
}

export function updateEventComponent(
eventComponentInstance: ReactEventComponentInstance<any, any, any>,
eventComponentInstance: ReactEventComponentInstance<any, any>,
) {
throw new Error('Not yet implemented.');
}

export function unmountEventComponent(
eventComponentInstance: ReactEventComponentInstance<any, any, any>,
eventComponentInstance: ReactEventComponentInstance<any, any>,
): void {
throw new Error('Not yet implemented.');
}
50 changes: 13 additions & 37 deletions packages/react-dom/src/client/ReactDOMComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,9 @@ import {registrationNameModules} from 'events/EventPluginRegistry';
import warning from 'shared/warning';
import {canUseDOM} from 'shared/ExecutionEnvironment';
import warningWithoutStack from 'shared/warningWithoutStack';
import type {ReactDOMEventResponderEventType} from 'shared/ReactDOMTypes';
import endsWith from 'shared/endsWith';
import type {DOMTopLevelEventType} from 'events/TopLevelEventTypes';
import {
setListenToResponderEventTypes,
generateListeningKey,
} from '../events/DOMEventResponderSystem';
import {setListenToResponderEventTypes} from '../events/DOMEventResponderSystem';

import {
getValueForAttribute,
Expand Down Expand Up @@ -1284,7 +1281,7 @@ export function restoreControlledState(
}

export function listenToEventResponderEventTypes(
eventTypes: Array<ReactDOMEventResponderEventType>,
eventTypes: Array<string>,
element: Element | Document,
): void {
if (enableFlareAPI) {
Expand All @@ -1294,40 +1291,19 @@ export function listenToEventResponderEventTypes(

// Go through each target event type of the event responder
for (let i = 0, length = eventTypes.length; i < length; ++i) {
const targetEventType = eventTypes[i];
let topLevelType;
let passive = true;

// If no event config object is provided (i.e. - only a string),
// we default to enabling passive and not capture.
if (typeof targetEventType === 'string') {
topLevelType = targetEventType;
} else {
if (__DEV__) {
warning(
typeof targetEventType === 'object' && targetEventType !== null,
'Event Responder: invalid entry in event types array. ' +
'Entry must be string or an object. Instead, got %s.',
targetEventType,
);
}
const targetEventConfigObject = ((targetEventType: any): {
name: string,
passive?: boolean,
});
topLevelType = targetEventConfigObject.name;
if (targetEventConfigObject.passive !== undefined) {
passive = targetEventConfigObject.passive;
}
}
const listeningName = generateListeningKey(topLevelType, passive);
if (!listeningSet.has(listeningName)) {
const eventType = eventTypes[i];
const isPassive = !endsWith(eventType, '_active');
const eventKey = isPassive ? eventType + '_passive' : eventType;
const targetEventType = isPassive
? eventType
: eventType.substring(0, eventType.length - 7);
if (!listeningSet.has(eventKey)) {
trapEventForResponderEventSystem(
element,
((topLevelType: any): DOMTopLevelEventType),
passive,
((targetEventType: any): DOMTopLevelEventType),
isPassive,
);
listeningSet.add(listeningName);
listeningSet.add(eventKey);
}
}
}
Expand Down
135 changes: 29 additions & 106 deletions packages/react-dom/src/events/DOMEventResponderSystem.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import type {EventPriority} from 'shared/ReactTypes';
import type {
ReactDOMEventResponder,
ReactDOMEventComponentInstance,
ReactDOMEventResponderEventType,
ReactDOMResponderContext,
ReactDOMResponderEvent,
} from 'shared/ReactDOMTypes';
Expand Down Expand Up @@ -95,10 +94,6 @@ const rootEventTypesToEventComponentInstances: Map<
DOMTopLevelEventType | string,
Set<ReactDOMEventComponentInstance>,
> = new Map();
const targetEventTypeCached: Map<
Array<ReactDOMEventResponderEventType>,
Set<string>,
> = new Map();
const ownershipChangeListeners: Set<ReactDOMEventComponentInstance> = new Set();
const PossiblyWeakMap = typeof WeakMap === 'function' ? WeakMap : Map;
const eventListeners:
Expand Down Expand Up @@ -248,9 +243,7 @@ const eventResponderContext: ReactDOMResponderContext = {
}
return false;
},
addRootEventTypes(
rootEventTypes: Array<ReactDOMEventResponderEventType>,
): void {
addRootEventTypes(rootEventTypes: Array<string>): void {
validateResponderContext();
const activeDocument = getActiveDocument();
listenToResponderEventTypesImpl(rootEventTypes, activeDocument);
Expand All @@ -260,37 +253,17 @@ const eventResponderContext: ReactDOMResponderContext = {
registerRootEventType(rootEventType, eventComponentInstance);
}
},
removeRootEventTypes(
rootEventTypes: Array<ReactDOMEventResponderEventType>,
): void {
removeRootEventTypes(rootEventTypes: Array<string>): void {
validateResponderContext();
for (let i = 0; i < rootEventTypes.length; i++) {
const rootEventType = rootEventTypes[i];
let name = rootEventType;
let passive = true;

if (typeof rootEventType !== 'string') {
const targetEventConfigObject = ((rootEventType: any): {
name: string,
passive?: boolean,
});
name = targetEventConfigObject.name;
if (targetEventConfigObject.passive !== undefined) {
passive = targetEventConfigObject.passive;
}
}

const listeningName = generateListeningKey(
((name: any): string),
passive,
);
let rootEventComponents = rootEventTypesToEventComponentInstances.get(
listeningName,
rootEventType,
);
let rootEventTypesSet = ((currentInstance: any): ReactDOMEventComponentInstance)
.rootEventTypes;
if (rootEventTypesSet !== null) {
rootEventTypesSet.delete(listeningName);
rootEventTypesSet.delete(rootEventType);
}
if (rootEventComponents !== undefined) {
rootEventComponents.delete(
Expand Down Expand Up @@ -595,41 +568,20 @@ function processEventQueue(): void {
}
}

function getDOMTargetEventTypesSet(
eventTypes: Array<ReactDOMEventResponderEventType>,
): Set<string> {
let cachedSet = targetEventTypeCached.get(eventTypes);

if (cachedSet === undefined) {
cachedSet = new Set();
for (let i = 0; i < eventTypes.length; i++) {
const eventType = eventTypes[i];
let name = eventType;
let passive = true;

if (typeof eventType !== 'string') {
const targetEventConfigObject = ((eventType: any): {
name: string,
passive?: boolean,
});
name = targetEventConfigObject.name;
if (targetEventConfigObject.passive !== undefined) {
passive = targetEventConfigObject.passive;
}
}
const listeningName = generateListeningKey(
((name: any): string),
passive,
);
cachedSet.add(listeningName);
function responderEventTypesContainType(
eventTypes: Array<string>,
type: string,
): boolean {
for (let i = 0, len = eventTypes.length; i < len; i++) {
if (eventTypes[i] === type) {
return true;
}
targetEventTypeCached.set(eventTypes, cachedSet);
}
return cachedSet;
return false;
}

function handleTargetEventResponderInstance(
listeningName: string,
eventType: string,
responderEvent: ReactDOMResponderEvent,
eventComponentInstance: ReactDOMEventComponentInstance,
hookComponentResponderValidation: null | Set<ReactDOMEventResponder>,
Expand All @@ -639,8 +591,7 @@ function handleTargetEventResponderInstance(
const targetEventTypes = responder.targetEventTypes;
// Validate the target event type exists on the responder
if (targetEventTypes !== undefined) {
const targetEventTypesSet = getDOMTargetEventTypesSet(targetEventTypes);
if (targetEventTypesSet.has(listeningName)) {
if (responderEventTypesContainType(targetEventTypes, eventType)) {
if (hookComponentResponderValidation !== null) {
hookComponentResponderValidation.add(responder);
}
Expand Down Expand Up @@ -700,25 +651,23 @@ function checkForLocalPropagationContinuation(
}

function traverseAndHandleEventResponderInstances(
topLevelType: DOMTopLevelEventType,
topLevelType: string,
targetFiber: null | Fiber,
nativeEvent: AnyNativeEvent,
nativeEventTarget: EventTarget,
eventSystemFlags: EventSystemFlags,
): void {
const isPassiveEvent = (eventSystemFlags & IS_PASSIVE) !== 0;
const isPassiveSupported = (eventSystemFlags & PASSIVE_NOT_SUPPORTED) === 0;
const listeningName = generateListeningKey(
((topLevelType: any): string),
isPassiveEvent || !isPassiveSupported,
);
const isPassive = isPassiveEvent || !isPassiveSupported;
const eventType = isPassive ? topLevelType : topLevelType + '_active';

// Trigger event responders in this order:
// - Bubble target phase
// - Root phase

const responderEvent = createDOMResponderEvent(
((topLevelType: any): string),
topLevelType,
nativeEvent,
((nativeEventTarget: any): Element | Document),
isPassiveEvent,
Expand All @@ -743,7 +692,7 @@ function traverseAndHandleEventResponderInstances(
// Switch to the current fiber tree
node = eventComponentInstance.currentFiber;
handleTargetEventResponderInstance(
listeningName,
eventType,
responderEvent,
eventComponentInstance,
hookComponentResponderValidation,
Expand All @@ -760,7 +709,7 @@ function traverseAndHandleEventResponderInstances(
)
) {
handleTargetEventResponderInstance(
listeningName,
eventType,
responderEvent,
eventComponentInstance,
null,
Expand All @@ -776,7 +725,7 @@ function traverseAndHandleEventResponderInstances(
responderEvent.currentTarget = null;
// Root phase
const rootEventInstances = rootEventTypesToEventComponentInstances.get(
listeningName,
eventType,
);
if (rootEventInstances !== undefined) {
const rootEventComponentInstances = Array.from(rootEventInstances);
Expand Down Expand Up @@ -906,7 +855,7 @@ function validateResponderContext(): void {
}

export function dispatchEventForResponderEventSystem(
topLevelType: DOMTopLevelEventType,
topLevelType: string,
targetFiber: null | Fiber,
nativeEvent: AnyNativeEvent,
nativeEventTarget: EventTarget,
Expand Down Expand Up @@ -950,7 +899,7 @@ export function dispatchEventForResponderEventSystem(

export function addRootEventTypesForComponentInstance(
eventComponentInstance: ReactDOMEventComponentInstance,
rootEventTypes: Array<ReactDOMEventResponderEventType>,
rootEventTypes: Array<string>,
): void {
for (let i = 0; i < rootEventTypes.length; i++) {
const rootEventType = rootEventTypes[i];
Expand All @@ -959,31 +908,16 @@ export function addRootEventTypesForComponentInstance(
}

function registerRootEventType(
rootEventType: ReactDOMEventResponderEventType,
rootEventType: string,
eventComponentInstance: ReactDOMEventComponentInstance,
): void {
let name = rootEventType;
let passive = true;

if (typeof rootEventType !== 'string') {
const targetEventConfigObject = ((rootEventType: any): {
name: string,
passive?: boolean,
});
name = targetEventConfigObject.name;
if (targetEventConfigObject.passive !== undefined) {
passive = targetEventConfigObject.passive;
}
}

const listeningName = generateListeningKey(((name: any): string), passive);
let rootEventComponentInstances = rootEventTypesToEventComponentInstances.get(
listeningName,
rootEventType,
);
if (rootEventComponentInstances === undefined) {
rootEventComponentInstances = new Set();
rootEventTypesToEventComponentInstances.set(
listeningName,
rootEventType,
rootEventComponentInstances,
);
}
Expand All @@ -992,23 +926,12 @@ function registerRootEventType(
rootEventTypesSet = eventComponentInstance.rootEventTypes = new Set();
}
invariant(
!rootEventTypesSet.has(listeningName),
!rootEventTypesSet.has(rootEventType),
'addRootEventTypes() found a duplicate root event ' +
'type of "%s". This might be because the event type exists in the event responder "rootEventTypes" ' +
'array or because of a previous addRootEventTypes() using this root event type.',
name,
rootEventType,
);
rootEventTypesSet.add(listeningName);
rootEventTypesSet.add(rootEventType);
rootEventComponentInstances.add(eventComponentInstance);
}

export function generateListeningKey(
topLevelType: string,
passive: boolean,
): string {
// Create a unique name for this event, plus its properties. We'll
// use this to ensure we don't listen to the same event with the same
// properties again.
const passiveKey = passive ? '_passive' : '_active';
return `${topLevelType}${passiveKey}`;
}
2 changes: 1 addition & 1 deletion packages/react-dom/src/events/ReactDOMEventListener.js
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ export function dispatchEvent(
} else {
// React Flare event system
dispatchEventForResponderEventSystem(
topLevelType,
(topLevelType: any),
targetInst,
nativeEvent,
nativeEventTarget,
Expand Down