From 67e3f3fb6e342f95f00215c84d5d013d7b0e1b33 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Mon, 8 Jul 2019 14:35:59 +0100 Subject: [PATCH] [Flare] Revise responder event types (#16081) --- packages/react-art/src/ReactARTHostConfig.js | 6 +- .../react-dom/src/client/ReactDOMComponent.js | 50 ++----- .../src/events/DOMEventResponderSystem.js | 135 ++++-------------- .../src/events/ReactDOMEventListener.js | 2 +- .../DOMEventResponderSystem-test.internal.js | 8 +- packages/react-events/src/dom/Drag.js | 17 ++- packages/react-events/src/dom/Focus.js | 5 +- packages/react-events/src/dom/FocusScope.js | 4 +- packages/react-events/src/dom/Press.js | 10 +- packages/react-events/src/dom/Swipe.js | 17 ++- packages/react-events/src/rn/Press.js | 2 - .../src/ReactFabricEventResponderSystem.js | 53 +++---- .../src/ReactFabricHostConfig.js | 2 - .../src/ReactNativeHostConfig.js | 2 - .../src/ReactNativeTypes.js | 21 +-- packages/react-reconciler/src/ReactFiber.js | 2 +- .../src/ReactFiberCompleteWork.js | 1 - .../react-reconciler/src/ReactFiberEvents.js | 10 +- .../react-reconciler/src/ReactFiberHooks.js | 14 +- .../src/ReactTestHostConfig.js | 6 +- packages/react/src/ReactHooks.js | 4 +- packages/shared/ReactDOMTypes.js | 14 +- packages/shared/ReactTypes.js | 16 +-- packages/shared/createEventComponent.js | 6 +- packages/shared/endsWith.js | 13 ++ 25 files changed, 141 insertions(+), 279 deletions(-) create mode 100644 packages/shared/endsWith.js diff --git a/packages/react-art/src/ReactARTHostConfig.js b/packages/react-art/src/ReactARTHostConfig.js index be516fe343517..58580f1f5ca85 100644 --- a/packages/react-art/src/ReactARTHostConfig.js +++ b/packages/react-art/src/ReactARTHostConfig.js @@ -428,19 +428,19 @@ export function unhideTextInstance(textInstance, text): void { } export function mountEventComponent( - eventComponentInstance: ReactEventComponentInstance, + eventComponentInstance: ReactEventComponentInstance, ) { throw new Error('Not yet implemented.'); } export function updateEventComponent( - eventComponentInstance: ReactEventComponentInstance, + eventComponentInstance: ReactEventComponentInstance, ) { throw new Error('Not yet implemented.'); } export function unmountEventComponent( - eventComponentInstance: ReactEventComponentInstance, + eventComponentInstance: ReactEventComponentInstance, ): void { throw new Error('Not yet implemented.'); } diff --git a/packages/react-dom/src/client/ReactDOMComponent.js b/packages/react-dom/src/client/ReactDOMComponent.js index fa77a2ef3572f..af580be510bee 100644 --- a/packages/react-dom/src/client/ReactDOMComponent.js +++ b/packages/react-dom/src/client/ReactDOMComponent.js @@ -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, @@ -1284,7 +1281,7 @@ export function restoreControlledState( } export function listenToEventResponderEventTypes( - eventTypes: Array, + eventTypes: Array, element: Element | Document, ): void { if (enableFlareAPI) { @@ -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); } } } diff --git a/packages/react-dom/src/events/DOMEventResponderSystem.js b/packages/react-dom/src/events/DOMEventResponderSystem.js index 9e4d66cafd6fd..7097e9a27a84f 100644 --- a/packages/react-dom/src/events/DOMEventResponderSystem.js +++ b/packages/react-dom/src/events/DOMEventResponderSystem.js @@ -21,7 +21,6 @@ import type {EventPriority} from 'shared/ReactTypes'; import type { ReactDOMEventResponder, ReactDOMEventComponentInstance, - ReactDOMEventResponderEventType, ReactDOMResponderContext, ReactDOMResponderEvent, } from 'shared/ReactDOMTypes'; @@ -95,10 +94,6 @@ const rootEventTypesToEventComponentInstances: Map< DOMTopLevelEventType | string, Set, > = new Map(); -const targetEventTypeCached: Map< - Array, - Set, -> = new Map(); const ownershipChangeListeners: Set = new Set(); const PossiblyWeakMap = typeof WeakMap === 'function' ? WeakMap : Map; const eventListeners: @@ -248,9 +243,7 @@ const eventResponderContext: ReactDOMResponderContext = { } return false; }, - addRootEventTypes( - rootEventTypes: Array, - ): void { + addRootEventTypes(rootEventTypes: Array): void { validateResponderContext(); const activeDocument = getActiveDocument(); listenToResponderEventTypesImpl(rootEventTypes, activeDocument); @@ -260,37 +253,17 @@ const eventResponderContext: ReactDOMResponderContext = { registerRootEventType(rootEventType, eventComponentInstance); } }, - removeRootEventTypes( - rootEventTypes: Array, - ): void { + removeRootEventTypes(rootEventTypes: Array): 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( @@ -595,41 +568,20 @@ function processEventQueue(): void { } } -function getDOMTargetEventTypesSet( - eventTypes: Array, -): Set { - 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, + 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, @@ -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); } @@ -700,7 +651,7 @@ function checkForLocalPropagationContinuation( } function traverseAndHandleEventResponderInstances( - topLevelType: DOMTopLevelEventType, + topLevelType: string, targetFiber: null | Fiber, nativeEvent: AnyNativeEvent, nativeEventTarget: EventTarget, @@ -708,17 +659,15 @@ function traverseAndHandleEventResponderInstances( ): 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, @@ -743,7 +692,7 @@ function traverseAndHandleEventResponderInstances( // Switch to the current fiber tree node = eventComponentInstance.currentFiber; handleTargetEventResponderInstance( - listeningName, + eventType, responderEvent, eventComponentInstance, hookComponentResponderValidation, @@ -760,7 +709,7 @@ function traverseAndHandleEventResponderInstances( ) ) { handleTargetEventResponderInstance( - listeningName, + eventType, responderEvent, eventComponentInstance, null, @@ -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); @@ -906,7 +855,7 @@ function validateResponderContext(): void { } export function dispatchEventForResponderEventSystem( - topLevelType: DOMTopLevelEventType, + topLevelType: string, targetFiber: null | Fiber, nativeEvent: AnyNativeEvent, nativeEventTarget: EventTarget, @@ -950,7 +899,7 @@ export function dispatchEventForResponderEventSystem( export function addRootEventTypesForComponentInstance( eventComponentInstance: ReactDOMEventComponentInstance, - rootEventTypes: Array, + rootEventTypes: Array, ): void { for (let i = 0; i < rootEventTypes.length; i++) { const rootEventType = rootEventTypes[i]; @@ -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, ); } @@ -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}`; -} diff --git a/packages/react-dom/src/events/ReactDOMEventListener.js b/packages/react-dom/src/events/ReactDOMEventListener.js index 775519e11b61d..d9b0eabd75996 100644 --- a/packages/react-dom/src/events/ReactDOMEventListener.js +++ b/packages/react-dom/src/events/ReactDOMEventListener.js @@ -334,7 +334,7 @@ export function dispatchEvent( } else { // React Flare event system dispatchEventForResponderEventSystem( - topLevelType, + (topLevelType: any), targetInst, nativeEvent, nativeEventTarget, diff --git a/packages/react-dom/src/events/__tests__/DOMEventResponderSystem-test.internal.js b/packages/react-dom/src/events/__tests__/DOMEventResponderSystem-test.internal.js index e0f4f2fde0f40..c30a8cf9e6db7 100644 --- a/packages/react-dom/src/events/__tests__/DOMEventResponderSystem-test.internal.js +++ b/packages/react-dom/src/events/__tests__/DOMEventResponderSystem-test.internal.js @@ -627,7 +627,7 @@ describe('DOMEventResponderSystem', () => { const buttonRef = React.createRef(); const ClickEventComponent1 = createReactEventComponent({ - targetEventTypes: [{name: 'click', passive: false, capture: false}], + targetEventTypes: ['click_active'], onEvent: event => { clickEventComponent1Fired++; eventLog.push({ @@ -639,7 +639,7 @@ describe('DOMEventResponderSystem', () => { }); const ClickEventComponent2 = createReactEventComponent({ - targetEventTypes: [{name: 'click', passive: true, capture: false}], + targetEventTypes: ['click'], onEvent: event => { clickEventComponent2Fired++; eventLog.push({ @@ -686,7 +686,7 @@ describe('DOMEventResponderSystem', () => { let eventLog = []; const ClickEventComponent1 = createReactEventComponent({ - rootEventTypes: [{name: 'click', passive: false, capture: false}], + rootEventTypes: ['click_active'], onRootEvent: event => { clickEventComponent1Fired++; eventLog.push({ @@ -698,7 +698,7 @@ describe('DOMEventResponderSystem', () => { }); const ClickEventComponent2 = createReactEventComponent({ - rootEventTypes: [{name: 'click', passive: true, capture: false}], + rootEventTypes: ['click'], onRootEvent: event => { clickEventComponent2Fired++; eventLog.push({ diff --git a/packages/react-events/src/dom/Drag.js b/packages/react-events/src/dom/Drag.js index 4b3847699c187..7dc0d301b04f2 100644 --- a/packages/react-events/src/dom/Drag.js +++ b/packages/react-events/src/dom/Drag.js @@ -18,11 +18,7 @@ import React from 'react'; import {DiscreteEvent, UserBlockingEvent} from 'shared/ReactTypes'; const targetEventTypes = ['pointerdown']; -const rootEventTypes = [ - 'pointerup', - 'pointercancel', - {name: 'pointermove', passive: false}, -]; +const rootEventTypes = ['pointerup', 'pointercancel', 'pointermove_active']; type DragState = { dragTarget: null | Element | Document, @@ -38,10 +34,13 @@ type DragState = { // too if (typeof window !== 'undefined' && window.PointerEvent === undefined) { targetEventTypes.push('touchstart', 'mousedown'); - rootEventTypes.push('mouseup', 'mousemove', 'touchend', 'touchcancel', { - name: 'touchmove', - passive: false, - }); + rootEventTypes.push( + 'mouseup', + 'mousemove', + 'touchend', + 'touchcancel', + 'touchmove_active', + ); } type EventData = { diff --git a/packages/react-events/src/dom/Focus.js b/packages/react-events/src/dom/Focus.js index 32c4101b4e6f6..04ee90fc7108c 100644 --- a/packages/react-events/src/dom/Focus.js +++ b/packages/react-events/src/dom/Focus.js @@ -46,10 +46,7 @@ const isMac = ? /^Mac/.test(window.navigator.platform) : false; -const targetEventTypes = [ - {name: 'focus', passive: true}, - {name: 'blur', passive: true}, -]; +const targetEventTypes = ['focus', 'blur']; const rootEventTypes = [ 'keydown', diff --git a/packages/react-events/src/dom/FocusScope.js b/packages/react-events/src/dom/FocusScope.js index c91ea5ef9c5ca..0d07639a23731 100644 --- a/packages/react-events/src/dom/FocusScope.js +++ b/packages/react-events/src/dom/FocusScope.js @@ -25,8 +25,8 @@ type FocusScopeState = { currentFocusedNode: null | HTMLElement, }; -const targetEventTypes = [{name: 'keydown', passive: false}]; -const rootEventTypes = [{name: 'focus', passive: true}]; +const targetEventTypes = ['keydown_active']; +const rootEventTypes = ['focus']; function focusElement(element: ?HTMLElement) { if (element != null) { diff --git a/packages/react-events/src/dom/Press.js b/packages/react-events/src/dom/Press.js index 5ead8e9a97c1b..75a85b9a46464 100644 --- a/packages/react-events/src/dom/Press.js +++ b/packages/react-events/src/dom/Press.js @@ -123,12 +123,12 @@ const DEFAULT_PRESS_RETENTION_OFFSET = { }; const targetEventTypes = [ - {name: 'keydown', passive: false}, - {name: 'contextmenu', passive: false}, + 'keydown_active', + 'contextmenu_active', // We need to preventDefault on pointerdown for mouse/pen events // that are in hit target area but not the element area. - {name: 'pointerdown', passive: false}, - {name: 'click', passive: false}, + 'pointerdown_active', + 'click_active', ]; const rootEventTypes = [ 'click', @@ -139,7 +139,7 @@ const rootEventTypes = [ 'pointercancel', // We listen to this here so stopPropagation can // block other mouseup events used internally - {name: 'mouseup', passive: false}, + 'mouseup_active', 'touchend', ]; diff --git a/packages/react-events/src/dom/Swipe.js b/packages/react-events/src/dom/Swipe.js index 2ec90c3c36103..f5d7ecf4b7929 100644 --- a/packages/react-events/src/dom/Swipe.js +++ b/packages/react-events/src/dom/Swipe.js @@ -18,20 +18,19 @@ import React from 'react'; import {UserBlockingEvent, DiscreteEvent} from 'shared/ReactTypes'; const targetEventTypes = ['pointerdown']; -const rootEventTypes = [ - 'pointerup', - 'pointercancel', - {name: 'pointermove', passive: false}, -]; +const rootEventTypes = ['pointerup', 'pointercancel', 'pointermove_active']; // In the case we don't have PointerEvents (Safari), we listen to touch events // too if (typeof window !== 'undefined' && window.PointerEvent === undefined) { targetEventTypes.push('touchstart', 'mousedown'); - rootEventTypes.push('mouseup', 'mousemove', 'touchend', 'touchcancel', { - name: 'touchmove', - passive: false, - }); + rootEventTypes.push( + 'mouseup', + 'mousemove', + 'touchend', + 'touchcancel', + 'touchmove_active', + ); } type EventData = { diff --git a/packages/react-events/src/rn/Press.js b/packages/react-events/src/rn/Press.js index 278f3622ce661..726c36300adcb 100644 --- a/packages/react-events/src/rn/Press.js +++ b/packages/react-events/src/rn/Press.js @@ -10,7 +10,6 @@ import type { ReactNativeResponderEvent, ReactNativeResponderContext, - ReactNativeEventResponderEventType, ReactNativeEventTarget, PointerType, ReactFaricEventTouch, @@ -24,7 +23,6 @@ import { } from 'react-native-renderer/src/ReactNativeTypes'; type ReactNativeEventResponder = ReactEventResponder< - ReactNativeEventResponderEventType, ReactNativeResponderEvent, ReactNativeResponderContext, >; diff --git a/packages/react-native-renderer/src/ReactFabricEventResponderSystem.js b/packages/react-native-renderer/src/ReactFabricEventResponderSystem.js index b670d75f2a9bd..0a68466868e91 100644 --- a/packages/react-native-renderer/src/ReactFabricEventResponderSystem.js +++ b/packages/react-native-renderer/src/ReactFabricEventResponderSystem.js @@ -23,7 +23,6 @@ import type { ReactEventComponentInstance, } from 'shared/ReactTypes'; import type { - ReactNativeEventResponderEventType, ReactNativeResponderContext, ReactNativeResponderEvent, EventPriority, @@ -74,13 +73,11 @@ type EventQueue = { }; type ReactNativeEventResponder = ReactEventResponder< - ReactNativeEventResponderEventType, ReactNativeResponderEvent, ReactNativeResponderContext, >; type ReactNativeEventComponentInstance = ReactEventComponentInstance< - ReactNativeEventResponderEventType, ReactNativeResponderEvent, ReactNativeResponderContext, >; @@ -89,13 +86,9 @@ const {measureInWindow} = nativeFabricUIManager; const activeTimeouts: Map = new Map(); const rootEventTypesToEventComponentInstances: Map< - ReactNativeEventResponderEventType | string, + string, Set, > = new Map(); -const targetEventTypeCached: Map< - Array, - Set, -> = new Map(); const ownershipChangeListeners: Set< ReactNativeEventComponentInstance, > = new Set(); @@ -214,9 +207,7 @@ const eventResponderContext: ReactNativeResponderContext = { }); }); }, - addRootEventTypes( - rootEventTypes: Array, - ): void { + addRootEventTypes(rootEventTypes: Array): void { validateResponderContext(); for (let i = 0; i < rootEventTypes.length; i++) { const rootEventType = rootEventTypes[i]; @@ -224,9 +215,7 @@ const eventResponderContext: ReactNativeResponderContext = { registerRootEventType(rootEventType, eventComponentInstance); } }, - removeRootEventTypes( - rootEventTypes: Array, - ): void { + removeRootEventTypes(rootEventTypes: Array): void { validateResponderContext(); for (let i = 0; i < rootEventTypes.length; i++) { const rootEventType = rootEventTypes[i]; @@ -330,7 +319,7 @@ function processTimers( } function createFabricResponderEvent( - topLevelType: ReactNativeEventResponderEventType, + topLevelType: string, nativeEvent: ReactFaricEvent, target: null | ReactNativeEventTarget, ): ReactNativeResponderEvent { @@ -451,19 +440,18 @@ function processEvents(events: Array): void { } } -function getFabricTargetEventTypesSet( - eventTypes: Array, -): Set { - let cachedSet = targetEventTypeCached.get(eventTypes); - - if (cachedSet === undefined) { - cachedSet = new Set(); - for (let i = 0; i < eventTypes.length; i++) { - cachedSet.add(eventTypes[i]); +// TODO this function is almost an exact copy of the DOM version, we should +// somehow share the logic +function responderEventTypesContainType( + eventTypes: Array, + 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; } // TODO this function is almost an exact copy of the DOM version, we should @@ -499,7 +487,7 @@ function checkForLocalPropagationContinuation( // TODO this function is almost an exact copy of the DOM version, we should // somehow share the logic function handleTargetEventResponderInstance( - topLevelType: ReactNativeEventResponderEventType, + topLevelType: string, responderEvent: ReactNativeResponderEvent, eventComponentInstance: ReactNativeEventComponentInstance, hookComponentResponderValidation: null | Set, @@ -509,8 +497,7 @@ function handleTargetEventResponderInstance( const targetEventTypes = responder.targetEventTypes; // Validate the target event type exists on the responder if (targetEventTypes !== undefined) { - const targetEventTypesSet = getFabricTargetEventTypesSet(targetEventTypes); - if (targetEventTypesSet.has(topLevelType)) { + if (responderEventTypesContainType(targetEventTypes, topLevelType)) { if (hookComponentResponderValidation !== null) { hookComponentResponderValidation.add(responder); } @@ -544,7 +531,7 @@ function handleTargetEventResponderInstance( // TODO this function is almost an exact copy of the DOM version, we should // somehow share the logic function traverseAndHandleEventResponderInstances( - topLevelType: ReactNativeEventResponderEventType, + topLevelType: string, targetFiber: null | Fiber, nativeEvent: ReactFaricEvent, ): void { @@ -642,7 +629,7 @@ function traverseAndHandleEventResponderInstances( // TODO this function is almost an exact copy of the DOM version, we should // somehow share the logic export function dispatchEventForResponderEventSystem( - topLevelType: ReactNativeEventResponderEventType, + topLevelType: string, targetFiber: null | Fiber, nativeEvent: ReactFaricEvent, ): void { @@ -745,7 +732,7 @@ export function unmountEventResponder( } function registerRootEventType( - rootEventType: ReactNativeEventResponderEventType, + rootEventType: string, eventComponentInstance: ReactNativeEventComponentInstance, ) { let rootEventComponentInstances = rootEventTypesToEventComponentInstances.get( @@ -775,7 +762,7 @@ function registerRootEventType( export function addRootEventTypesForComponentInstance( eventComponentInstance: ReactNativeEventComponentInstance, - rootEventTypes: Array, + rootEventTypes: Array, ): void { for (let i = 0; i < rootEventTypes.length; i++) { const rootEventType = rootEventTypes[i]; diff --git a/packages/react-native-renderer/src/ReactFabricHostConfig.js b/packages/react-native-renderer/src/ReactFabricHostConfig.js index 2e096ea03c35e..49a26bc870fb4 100644 --- a/packages/react-native-renderer/src/ReactFabricHostConfig.js +++ b/packages/react-native-renderer/src/ReactFabricHostConfig.js @@ -13,7 +13,6 @@ import type { MeasureOnSuccessCallback, NativeMethodsMixinType, ReactNativeBaseComponentViewConfig, - ReactNativeEventResponderEventType, ReactNativeResponderEvent, ReactNativeResponderContext, } from './ReactNativeTypes'; @@ -66,7 +65,6 @@ const {get: getViewConfigForType} = ReactNativeViewConfigRegistry; let nextReactTag = 2; type ReactNativeEventComponentInstance = ReactEventComponentInstance< - ReactNativeEventResponderEventType, ReactNativeResponderEvent, ReactNativeResponderContext, >; diff --git a/packages/react-native-renderer/src/ReactNativeHostConfig.js b/packages/react-native-renderer/src/ReactNativeHostConfig.js index 1726f572db6c6..fd8ba518ad534 100644 --- a/packages/react-native-renderer/src/ReactNativeHostConfig.js +++ b/packages/react-native-renderer/src/ReactNativeHostConfig.js @@ -9,7 +9,6 @@ import type { ReactNativeBaseComponentViewConfig, - ReactNativeEventResponderEventType, ReactNativeResponderEvent, ReactNativeResponderContext, } from './ReactNativeTypes'; @@ -35,7 +34,6 @@ import ReactNativeFiberHostComponent from './ReactNativeFiberHostComponent'; const {get: getViewConfigForType} = ReactNativeViewConfigRegistry; type ReactNativeEventComponentInstance = ReactEventComponentInstance< - ReactNativeEventResponderEventType, ReactNativeResponderEvent, ReactNativeResponderContext, >; diff --git a/packages/react-native-renderer/src/ReactNativeTypes.js b/packages/react-native-renderer/src/ReactNativeTypes.js index 923915f11fb1c..24b93a56c4d17 100644 --- a/packages/react-native-renderer/src/ReactNativeTypes.js +++ b/packages/react-native-renderer/src/ReactNativeTypes.js @@ -157,17 +157,6 @@ export type ReactFabricType = { __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: SecretInternalsFabricType, }; -export type ReactNativeEventResponderEventType = - | 'topMouseDown' - | 'topMouseMove' - | 'topMouseUp' - | 'topScroll' - | 'topSelectionChange' - | 'topTouchCancel' - | 'topTouchEnd' - | 'topTouchMove' - | 'topTouchStart'; - export type ReactNativeEventTarget = { node: Object, canonical: { @@ -202,7 +191,7 @@ export type ReactNativeResponderEvent = { currentTarget: null | ReactNativeEventTarget, nativeEvent: ReactFaricEvent, target: null | ReactNativeEventTarget, - type: ReactNativeEventResponderEventType, + type: string, }; export type ReactNativeResponderContext = { @@ -224,12 +213,8 @@ export type ReactNativeResponderContext = { bottom: number, }) => void, ): void, - addRootEventTypes: ( - rootEventTypes: Array, - ) => void, - removeRootEventTypes: ( - rootEventTypes: Array, - ) => void, + addRootEventTypes: (rootEventTypes: Array) => void, + removeRootEventTypes: (rootEventTypes: Array) => void, setTimeout: (func: () => void, timeout: number) => number, clearTimeout: (timerId: number) => void, getTimeStamp: () => number, diff --git a/packages/react-reconciler/src/ReactFiber.js b/packages/react-reconciler/src/ReactFiber.js index 5188146701607..4821f4205e28c 100644 --- a/packages/react-reconciler/src/ReactFiber.js +++ b/packages/react-reconciler/src/ReactFiber.js @@ -103,7 +103,7 @@ if (__DEV__) { export type Dependencies = { expirationTime: ExpirationTime, firstContext: ContextDependency | null, - events: Array> | null, + events: Array> | null, }; // A Fiber is work on a Component that needs to be done or was done. There can diff --git a/packages/react-reconciler/src/ReactFiberCompleteWork.js b/packages/react-reconciler/src/ReactFiberCompleteWork.js index 99dcf52d5df0e..d115261715253 100644 --- a/packages/react-reconciler/src/ReactFiberCompleteWork.js +++ b/packages/react-reconciler/src/ReactFiberCompleteWork.js @@ -1126,7 +1126,6 @@ function completeWork( let eventComponentInstance: ReactEventComponentInstance< any, any, - any, > | null = workInProgress.stateNode; diff --git a/packages/react-reconciler/src/ReactFiberEvents.js b/packages/react-reconciler/src/ReactFiberEvents.js index 9a0e7f7a23db8..98f60789a00e7 100644 --- a/packages/react-reconciler/src/ReactFiberEvents.js +++ b/packages/react-reconciler/src/ReactFiberEvents.js @@ -32,8 +32,8 @@ export function prepareToReadEventComponents(workInProgress: Fiber): void { currentEventComponentInstanceIndex = 0; } -export function updateEventComponentInstance( - eventComponent: ReactEventComponent, +export function updateEventComponentInstance( + eventComponent: ReactEventComponent, props: Object, ): void { const responder = eventComponent.responder; @@ -82,14 +82,14 @@ export function updateEventComponentInstance( } } -export function createEventComponentInstance( +export function createEventComponentInstance( currentFiber: Fiber, props: Object, - responder: ReactEventResponder, + responder: ReactEventResponder, rootInstance: mixed, state: Object, isHook: boolean, -): ReactEventComponentInstance { +): ReactEventComponentInstance { return { currentFiber, isHook, diff --git a/packages/react-reconciler/src/ReactFiberHooks.js b/packages/react-reconciler/src/ReactFiberHooks.js index 0f391c0b0cd16..451b8a9cda5a1 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.js +++ b/packages/react-reconciler/src/ReactFiberHooks.js @@ -83,8 +83,8 @@ export type Dispatcher = { deps: Array | void | null, ): void, useDebugValue(value: T, formatterFn: ?(value: T) => mixed): void, - useEvent( - eventComponent: ReactEventComponent, + useEvent( + eventComponent: ReactEventComponent, props: Object, ): void, }; @@ -1416,7 +1416,7 @@ if (__DEV__) { mountHookTypesDev(); return mountDebugValue(value, formatterFn); }, - useEvent(eventComponent: ReactEventComponent, props) { + useEvent(eventComponent: ReactEventComponent, props) { currentHookNameInDev = 'useEvent'; mountHookTypesDev(); updateEventComponentInstance(eventComponent, props); @@ -1518,7 +1518,7 @@ if (__DEV__) { updateHookTypesDev(); return mountDebugValue(value, formatterFn); }, - useEvent(eventComponent: ReactEventComponent, props) { + useEvent(eventComponent: ReactEventComponent, props) { currentHookNameInDev = 'useEvent'; updateHookTypesDev(); updateEventComponentInstance(eventComponent, props); @@ -1620,7 +1620,7 @@ if (__DEV__) { updateHookTypesDev(); return updateDebugValue(value, formatterFn); }, - useEvent(eventComponent: ReactEventComponent, props) { + useEvent(eventComponent: ReactEventComponent, props) { currentHookNameInDev = 'useEvent'; updateHookTypesDev(); updateEventComponentInstance(eventComponent, props); @@ -1733,7 +1733,7 @@ if (__DEV__) { mountHookTypesDev(); return mountDebugValue(value, formatterFn); }, - useEvent(eventComponent: ReactEventComponent, props) { + useEvent(eventComponent: ReactEventComponent, props) { currentHookNameInDev = 'useEvent'; warnInvalidHookAccess(); mountHookTypesDev(); @@ -1847,7 +1847,7 @@ if (__DEV__) { updateHookTypesDev(); return updateDebugValue(value, formatterFn); }, - useEvent(eventComponent: ReactEventComponent, props) { + useEvent(eventComponent: ReactEventComponent, props) { currentHookNameInDev = 'useEvent'; warnInvalidHookAccess(); updateHookTypesDev(); diff --git a/packages/react-test-renderer/src/ReactTestHostConfig.js b/packages/react-test-renderer/src/ReactTestHostConfig.js index 512cd0eb0ef28..467fc4550c335 100644 --- a/packages/react-test-renderer/src/ReactTestHostConfig.js +++ b/packages/react-test-renderer/src/ReactTestHostConfig.js @@ -286,19 +286,19 @@ export function unhideTextInstance( } export function mountEventComponent( - eventComponentInstance: ReactEventComponentInstance, + eventComponentInstance: ReactEventComponentInstance, ): void { // noop } export function updateEventComponent( - eventComponentInstance: ReactEventComponentInstance, + eventComponentInstance: ReactEventComponentInstance, ): void { // noop } export function unmountEventComponent( - eventComponentInstance: ReactEventComponentInstance, + eventComponentInstance: ReactEventComponentInstance, ): void { // noop } diff --git a/packages/react/src/ReactHooks.js b/packages/react/src/ReactHooks.js index 1f671715985e9..6744b9360a319 100644 --- a/packages/react/src/ReactHooks.js +++ b/packages/react/src/ReactHooks.js @@ -138,8 +138,8 @@ export function useDebugValue(value: any, formatterFn: ?(value: any) => any) { export const emptyObject = {}; -export function useEvent( - eventComponent: ReactEventComponent, +export function useEvent( + eventComponent: ReactEventComponent, props: null | Object, ) { const dispatcher = resolveDispatcher(); diff --git a/packages/shared/ReactDOMTypes.js b/packages/shared/ReactDOMTypes.js index e13331ca2c356..013ebed24d0bd 100644 --- a/packages/shared/ReactDOMTypes.js +++ b/packages/shared/ReactDOMTypes.js @@ -15,10 +15,6 @@ import type { type AnyNativeEvent = Event | KeyboardEvent | MouseEvent | Touch; -export type ReactDOMEventResponderEventType = - | string - | {name: string, passive?: boolean}; - export type PointerType = | '' | 'mouse' @@ -39,13 +35,11 @@ export type ReactDOMResponderEvent = { }; export type ReactDOMEventResponder = ReactEventResponder< - ReactDOMEventResponderEventType, ReactDOMResponderEvent, ReactDOMResponderContext, >; export type ReactDOMEventComponentInstance = ReactEventComponentInstance< - ReactDOMEventResponderEventType, ReactDOMResponderEvent, ReactDOMResponderContext, >; @@ -62,12 +56,8 @@ export type ReactDOMResponderContext = { ) => boolean, isTargetWithinEventComponent: (Element | Document) => boolean, isTargetWithinEventResponderScope: (Element | Document) => boolean, - addRootEventTypes: ( - rootEventTypes: Array, - ) => void, - removeRootEventTypes: ( - rootEventTypes: Array, - ) => void, + addRootEventTypes: (rootEventTypes: Array) => void, + removeRootEventTypes: (rootEventTypes: Array) => void, hasOwnership: () => boolean, requestGlobalOwnership: () => boolean, releaseOwnership: () => boolean, diff --git a/packages/shared/ReactTypes.js b/packages/shared/ReactTypes.js index 1090d1a67a4c7..ec6f83981d5c5 100644 --- a/packages/shared/ReactTypes.js +++ b/packages/shared/ReactTypes.js @@ -14,7 +14,7 @@ export type ReactNode = | ReactFragment | ReactProvider | ReactConsumer - | ReactEventComponent; + | ReactEventComponent; export type ReactEmpty = null | void | boolean; @@ -80,20 +80,20 @@ export type RefObject = {| current: any, |}; -export type ReactEventComponentInstance = {| +export type ReactEventComponentInstance = {| currentFiber: mixed, isHook: boolean, props: Object, - responder: ReactEventResponder, + responder: ReactEventResponder, rootEventTypes: null | Set, rootInstance: null | mixed, state: Object, |}; -export type ReactEventResponder = { +export type ReactEventResponder = { displayName: string, - targetEventTypes?: Array, - rootEventTypes?: Array, + targetEventTypes?: Array, + rootEventTypes?: Array, getInitialState?: (props: Object) => Object, allowMultipleHostChildren: boolean, allowEventHooks: boolean, @@ -104,9 +104,9 @@ export type ReactEventResponder = { onOwnershipChange?: (context: C, props: Object, state: Object) => void, }; -export type ReactEventComponent = {| +export type ReactEventComponent = {| $$typeof: Symbol | number, - responder: ReactEventResponder, + responder: ReactEventResponder, |}; export opaque type EventPriority = 0 | 1 | 2; diff --git a/packages/shared/createEventComponent.js b/packages/shared/createEventComponent.js index faea980806c3b..622e9d6b2fbcb 100644 --- a/packages/shared/createEventComponent.js +++ b/packages/shared/createEventComponent.js @@ -28,9 +28,9 @@ if (__DEV__) { } } -export default function createEventComponent( - responder: ReactEventResponder, -): ReactEventComponent { +export default function createEventComponent( + responder: ReactEventResponder, +): ReactEventComponent { // We use responder as a Map key later on. When we have a bad // polyfill, then we can't use it as a key as the polyfill tries // to add a property to the object. diff --git a/packages/shared/endsWith.js b/packages/shared/endsWith.js new file mode 100644 index 0000000000000..115e75a9746f3 --- /dev/null +++ b/packages/shared/endsWith.js @@ -0,0 +1,13 @@ +/** + * 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 + */ + +export default function endsWith(subject: string, search: string): boolean { + const length = subject.length; + return subject.substring(length - search.length, length) === search; +}