From fe0d06098b1f5a31846714aa6aa1645fe9637eb2 Mon Sep 17 00:00:00 2001 From: Andrew Clark Date: Mon, 30 Mar 2020 19:10:11 -0700 Subject: [PATCH] Revert "ReactDOM.useEvent: Add support for experimental scopes API (#18375)" This reverts commit a16b34974508cd23ce0844ad09a0e37a879d5591. * ReactDOM.useEvent: Add support for experimental scopes API --- .../react-debug-tools/src/ReactDebugHooks.js | 6 +- .../src/client/ReactDOMHostConfig.js | 37 ++--- .../src/events/DOMModernPluginEventSystem.js | 115 +++------------ ...OMModernPluginEventSystem-test.internal.js | 133 ------------------ .../src/events/accumulateTwoPhaseListeners.js | 40 +----- .../react-dom/src/shared/ReactDOMTypes.js | 8 +- .../react-reconciler/src/ReactFiberHooks.js | 13 +- 7 files changed, 45 insertions(+), 307 deletions(-) diff --git a/packages/react-debug-tools/src/ReactDebugHooks.js b/packages/react-debug-tools/src/ReactDebugHooks.js index a338dd0526cf9..175b51485d30c 100644 --- a/packages/react-debug-tools/src/ReactDebugHooks.js +++ b/packages/react-debug-tools/src/ReactDebugHooks.js @@ -15,7 +15,6 @@ import type { ReactProviderType, ReactEventResponder, ReactEventResponderListener, - ReactScopeMethods, } from 'shared/ReactTypes'; import type {Fiber} from 'react-reconciler/src/ReactFiber'; import type {Hook, TimeoutConfig} from 'react-reconciler/src/ReactFiberHooks'; @@ -45,10 +44,7 @@ type HookLogEntry = { type ReactDebugListenerMap = {| clear: () => void, - setListener: ( - target: EventTarget | ReactScopeMethods, - callback: ?(Event) => void, - ) => void, + setListener: (target: EventTarget, callback: ?(Event) => void) => void, |}; let hookLog: Array = []; diff --git a/packages/react-dom/src/client/ReactDOMHostConfig.js b/packages/react-dom/src/client/ReactDOMHostConfig.js index a183b88481e31..099eded56ebf2 100644 --- a/packages/react-dom/src/client/ReactDOMHostConfig.js +++ b/packages/react-dom/src/client/ReactDOMHostConfig.js @@ -9,15 +9,6 @@ import type {TopLevelType} from 'legacy-events/TopLevelEventTypes'; import type {RootType} from './ReactDOMRoot'; -import type { - ReactDOMEventResponder, - ReactDOMEventResponderInstance, - ReactDOMFundamentalComponentInstance, - ReactDOMListener, - ReactDOMListenerEvent, - ReactDOMListenerMap, -} from '../shared/ReactDOMTypes'; -import type {ReactScopeMethods} from 'shared/ReactTypes'; import { precacheFiberNode, @@ -58,6 +49,14 @@ import { } from '../shared/HTMLNodeType'; import dangerousStyleValue from '../shared/dangerousStyleValue'; +import type { + ReactDOMEventResponder, + ReactDOMEventResponderInstance, + ReactDOMFundamentalComponentInstance, + ReactDOMListener, + ReactDOMListenerEvent, + ReactDOMListenerMap, +} from '../shared/ReactDOMTypes'; import { mountEventResponder, unmountEventResponder, @@ -70,7 +69,6 @@ import { enableDeprecatedFlareAPI, enableFundamentalAPI, enableUseEventAPI, - enableScopeAPI, } from 'shared/ReactFeatureFlags'; import {HostComponent} from 'react-reconciler/src/ReactWorkTags'; import { @@ -81,13 +79,10 @@ import { isManagedDOMElement, isValidEventTarget, listenToTopLevelEvent, - attachListenerToManagedDOMElement, detachListenerFromManagedDOMElement, - attachTargetEventListener, + attachListenerFromManagedDOMElement, detachTargetEventListener, - isReactScope, - attachListenerToReactScope, - detachListenerFromReactScope, + attachTargetEventListener, } from '../events/DOMModernPluginEventSystem'; import {getListenerMapForElement} from '../events/DOMEventListenerMap'; import {TOP_BEFORE_BLUR, TOP_AFTER_BLUR} from '../events/DOMTopLevelEventTypes'; @@ -1164,9 +1159,7 @@ export function mountEventListener(listener: ReactDOMListener): void { if (enableUseEventAPI) { const {target} = listener; if (isManagedDOMElement(target)) { - attachListenerToManagedDOMElement(listener); - } else if (enableScopeAPI && isReactScope(target)) { - attachListenerToReactScope(listener); + attachListenerFromManagedDOMElement(listener); } else { attachTargetEventListener(listener); } @@ -1178,8 +1171,6 @@ export function unmountEventListener(listener: ReactDOMListener): void { const {target} = listener; if (isManagedDOMElement(target)) { detachListenerFromManagedDOMElement(listener); - } else if (enableScopeAPI && isReactScope(target)) { - detachListenerFromReactScope(listener); } else { detachTargetEventListener(listener); } @@ -1187,15 +1178,13 @@ export function unmountEventListener(listener: ReactDOMListener): void { } export function validateEventListenerTarget( - target: EventTarget | ReactScopeMethods, + target: EventTarget, listener: ?(Event) => void, ): boolean { if (enableUseEventAPI) { if ( target != null && - (isManagedDOMElement(target) || - isValidEventTarget(target) || - isReactScope(target)) + (isManagedDOMElement(target) || isValidEventTarget(target)) ) { if (listener == null || typeof listener === 'function') { return true; diff --git a/packages/react-dom/src/events/DOMModernPluginEventSystem.js b/packages/react-dom/src/events/DOMModernPluginEventSystem.js index da24fc82b7b0e..e550cdb7a3469 100644 --- a/packages/react-dom/src/events/DOMModernPluginEventSystem.js +++ b/packages/react-dom/src/events/DOMModernPluginEventSystem.js @@ -14,7 +14,7 @@ import type { ElementListenerMapEntry, } from '../events/DOMEventListenerMap'; import type {EventSystemFlags} from 'legacy-events/EventSystemFlags'; -import type {EventPriority, ReactScopeMethods} from 'shared/ReactTypes'; +import type {EventPriority} from 'shared/ReactTypes'; import type {Fiber} from 'react-reconciler/src/ReactFiber'; import type {PluginModule} from 'legacy-events/PluginModuleType'; import type { @@ -142,11 +142,8 @@ const emptyDispatchConfigForCustomEvents: CustomDispatchConfig = { const isArray = Array.isArray; -// TODO: we should remove the FlowFixMes and the casting to figure out how to make -// these patterns work properly. -// $FlowFixMe: Flow struggles with this pattern, so we also have to cast it. -const PossiblyWeakMap = ((typeof WeakMap === 'function' ? WeakMap : Map): any); - +// $FlowFixMe: Flow struggles with this pattern +const PossiblyWeakMap = typeof WeakMap === 'function' ? WeakMap : Map; // $FlowFixMe: Flow cannot handle polymorphic WeakMaps export const eventTargetEventListenerStore: WeakMap< EventTarget, @@ -156,15 +153,6 @@ export const eventTargetEventListenerStore: WeakMap< >, > = new PossiblyWeakMap(); -// $FlowFixMe: Flow cannot handle polymorphic WeakMaps -export const reactScopeListenerStore: WeakMap< - ReactScopeMethods, - Map< - DOMTopLevelEventType, - {bubbled: Set, captured: Set}, - >, -> = new PossiblyWeakMap(); - function dispatchEventsForPlugins( topLevelType: DOMTopLevelEventType, eventSystemFlags: EventSystemFlags, @@ -318,20 +306,12 @@ function isMatchingRootContainer( ); } -export function isManagedDOMElement( - target: EventTarget | ReactScopeMethods, -): boolean { +export function isManagedDOMElement(target: EventTarget): boolean { return getClosestInstanceFromNode(((target: any): Node)) !== null; } -export function isValidEventTarget( - target: EventTarget | ReactScopeMethods, -): boolean { - return typeof (target: any).addEventListener === 'function'; -} - -export function isReactScope(target: EventTarget | ReactScopeMethods): boolean { - return typeof (target: any).getChildContextValues === 'function'; +export function isValidEventTarget(target: EventTarget): boolean { + return typeof target.addEventListener === 'function'; } export function dispatchEventForPluginEventSystem( @@ -466,16 +446,18 @@ function addEventTypeToDispatchConfig(type: DOMTopLevelEventType): void { } } -export function attachListenerToManagedDOMElement( +export function attachListenerFromManagedDOMElement( listener: ReactDOMListener, ): void { const {event, target} = listener; const {passive, priority, type} = event; - - const managedTargetElement = ((target: any): Element); - const containerEventTarget = getNearestRootOrPortalContainer( - managedTargetElement, - ); + const possibleManagedTarget = ((target: any): Element); + let containerEventTarget = target; + if (getClosestInstanceFromNode(possibleManagedTarget)) { + containerEventTarget = getNearestRootOrPortalContainer( + possibleManagedTarget, + ); + } const listenerMap = getListenerMapForElement(containerEventTarget); // Add the event listener to the target container (falling back to // the target if we didn't find one). @@ -487,11 +469,11 @@ export function attachListenerToManagedDOMElement( priority, ); // Get the internal listeners Set from the target instance. - let listeners = getListenersFromTarget(managedTargetElement); + let listeners = getListenersFromTarget(target); // If we don't have any listeners, then we need to init them. if (listeners === null) { listeners = new Set(); - initListenersSet(managedTargetElement, listeners); + initListenersSet(target, listeners); } // Add our listener to the listeners Set. listeners.add(listener); @@ -503,9 +485,8 @@ export function detachListenerFromManagedDOMElement( listener: ReactDOMListener, ): void { const {target} = listener; - const managedTargetElement = ((target: any): Element); // Get the internal listeners Set from the target instance. - const listeners = getListenersFromTarget(managedTargetElement); + const listeners = getListenersFromTarget(target); if (listeners !== null) { // Remove out listener from the listeners Set. listeners.delete(listener); @@ -515,21 +496,13 @@ export function detachListenerFromManagedDOMElement( export function attachTargetEventListener(listener: ReactDOMListener): void { const {event, target} = listener; const {capture, passive, priority, type} = event; - const eventTarget = ((target: any): EventTarget); - const listenerMap = getListenerMapForElement(eventTarget); + const listenerMap = getListenerMapForElement(target); // Add the event listener to the TargetEvent object. - listenToTopLevelEvent( - type, - eventTarget, - listenerMap, - passive, - priority, - capture, - ); - let eventTypeMap = eventTargetEventListenerStore.get(eventTarget); + listenToTopLevelEvent(type, target, listenerMap, passive, priority, capture); + let eventTypeMap = eventTargetEventListenerStore.get(target); if (eventTypeMap === undefined) { eventTypeMap = new Map(); - eventTargetEventListenerStore.set(eventTarget, eventTypeMap); + eventTargetEventListenerStore.set(target, eventTypeMap); } // Get the listeners by the event type let listeners = eventTypeMap.get(type); @@ -550,51 +523,7 @@ export function attachTargetEventListener(listener: ReactDOMListener): void { export function detachTargetEventListener(listener: ReactDOMListener): void { const {event, target} = listener; const {capture, type} = event; - const validEventTarget = ((target: any): EventTarget); - const eventTypeMap = eventTargetEventListenerStore.get(validEventTarget); - if (eventTypeMap !== undefined) { - const listeners = eventTypeMap.get(type); - if (listeners !== undefined) { - // Remove out listener from the listeners Set. - if (capture) { - listeners.captured.delete(listener); - } else { - listeners.bubbled.delete(listener); - } - } - } -} - -export function attachListenerToReactScope(listener: ReactDOMListener): void { - const {event, target} = listener; - const {capture, type} = event; - const reactScope = ((target: any): ReactScopeMethods); - let eventTypeMap = reactScopeListenerStore.get(reactScope); - if (eventTypeMap === undefined) { - eventTypeMap = new Map(); - reactScopeListenerStore.set(reactScope, eventTypeMap); - } - // Get the listeners by the event type - let listeners = eventTypeMap.get(type); - if (listeners === undefined) { - listeners = {captured: new Set(), bubbled: new Set()}; - eventTypeMap.set(type, listeners); - } - // Add our listener to the listeners Set. - if (capture) { - listeners.captured.add(listener); - } else { - listeners.bubbled.add(listener); - } - // Finally, add the event to our known event types list. - addEventTypeToDispatchConfig(type); -} - -export function detachListenerFromReactScope(listener: ReactDOMListener): void { - const {event, target} = listener; - const {capture, type} = event; - const reactScope = ((target: any): ReactScopeMethods); - const eventTypeMap = reactScopeListenerStore.get(reactScope); + const eventTypeMap = eventTargetEventListenerStore.get(target); if (eventTypeMap !== undefined) { const listeners = eventTypeMap.get(type); if (listeners !== undefined) { diff --git a/packages/react-dom/src/events/__tests__/DOMModernPluginEventSystem-test.internal.js b/packages/react-dom/src/events/__tests__/DOMModernPluginEventSystem-test.internal.js index 3c17d6bf86afb..0547c66a2adde 100644 --- a/packages/react-dom/src/events/__tests__/DOMModernPluginEventSystem-test.internal.js +++ b/packages/react-dom/src/events/__tests__/DOMModernPluginEventSystem-test.internal.js @@ -2327,139 +2327,6 @@ describe('DOMModernPluginEventSystem', () => { document.body.removeChild(container2); }, ); - - describe('Compatibility with Scopes API', () => { - beforeEach(() => { - jest.resetModules(); - ReactFeatureFlags = require('shared/ReactFeatureFlags'); - ReactFeatureFlags.enableModernEventSystem = true; - ReactFeatureFlags.enableUseEventAPI = true; - ReactFeatureFlags.enableScopeAPI = true; - - React = require('react'); - ReactDOM = require('react-dom'); - Scheduler = require('scheduler'); - ReactDOMServer = require('react-dom/server'); - }); - - it('handle propagation of click events on a scope', () => { - const buttonRef = React.createRef(); - const log = []; - const onClick = jest.fn(e => - log.push(['bubble', e.currentTarget]), - ); - const onClickCapture = jest.fn(e => - log.push(['capture', e.currentTarget]), - ); - const TestScope = React.unstable_createScope(); - - function Test() { - const click = ReactDOM.unstable_useEvent('click'); - const clickCapture = ReactDOM.unstable_useEvent('click', { - capture: true, - }); - const scopeRef = React.useRef(null); - - React.useEffect(() => { - click.setListener(scopeRef.current, onClick); - clickCapture.setListener(scopeRef.current, onClickCapture); - }); - - return ( - - - - ); - } - - ReactDOM.render(, container); - Scheduler.unstable_flushAll(); - - const buttonElement = buttonRef.current; - dispatchClickEvent(buttonElement); - - expect(onClick).toHaveBeenCalledTimes(2); - expect(onClickCapture).toHaveBeenCalledTimes(2); - expect(log).toEqual([ - ['capture', buttonElement], - ['capture', buttonElement], - ['bubble', buttonElement], - ['bubble', buttonElement], - ]); - - log.length = 0; - onClick.mockClear(); - onClickCapture.mockClear(); - - const divElement = divRef.current; - dispatchClickEvent(divElement); - - expect(onClick).toHaveBeenCalledTimes(3); - expect(onClickCapture).toHaveBeenCalledTimes(3); - expect(log).toEqual([ - ['capture', buttonElement], - ['capture', buttonElement], - ['capture', divElement], - ['bubble', divElement], - ['bubble', buttonElement], - ['bubble', buttonElement], - ]); - }); - }); }); }, ); diff --git a/packages/react-dom/src/events/accumulateTwoPhaseListeners.js b/packages/react-dom/src/events/accumulateTwoPhaseListeners.js index 8ef02e733cf5f..3a275eee17f25 100644 --- a/packages/react-dom/src/events/accumulateTwoPhaseListeners.js +++ b/packages/react-dom/src/events/accumulateTwoPhaseListeners.js @@ -11,19 +11,13 @@ import type {DOMTopLevelEventType} from 'legacy-events/TopLevelEventTypes'; import type {EventSystemFlags} from 'legacy-events/EventSystemFlags'; import type {ReactSyntheticEvent} from 'legacy-events/ReactSyntheticEventType'; -import { - HostComponent, - ScopeComponent, -} from 'react-reconciler/src/ReactWorkTags'; -import {enableUseEventAPI, enableScopeAPI} from 'shared/ReactFeatureFlags'; +import {HostComponent} from 'react-reconciler/src/ReactWorkTags'; +import {enableUseEventAPI} from 'shared/ReactFeatureFlags'; import getListener from 'legacy-events/getListener'; import {getListenersFromTarget} from '../client/ReactDOMComponentTree'; import {IS_TARGET_EVENT_ONLY} from 'legacy-events/EventSystemFlags'; -import { - eventTargetEventListenerStore, - reactScopeListenerStore, -} from './DOMModernPluginEventSystem'; +import {eventTargetEventListenerStore} from './DOMModernPluginEventSystem'; export default function accumulateTwoPhaseListeners( event: ReactSyntheticEvent, @@ -82,13 +76,11 @@ export default function accumulateTwoPhaseListeners( // usual two phase accumulation using the React fiber tree to pick up // all relevant useEvent and on* prop events. let node = event._targetInst; - let lastHostComponent; // Accumulate all instances and listeners via the target -> root path. while (node !== null) { - // Handle listeners that are on HostComponents (i.e.
) + // We only care for listeners that are on HostComponents (i.e.
) if (node.tag === HostComponent) { - lastHostComponent = node.stateNode; // For useEvent listenrs if (enableUseEventAPI && accumulateUseEventListeners) { // useEvent event listeners @@ -135,30 +127,6 @@ export default function accumulateTwoPhaseListeners( dispatchInstances.push(node); } } - } else if (enableScopeAPI && node.tag === ScopeComponent) { - const reactScope = node.stateNode.methods; - const eventTypeMap = reactScopeListenerStore.get(reactScope); - if (eventTypeMap !== undefined) { - const type = ((event.type: any): DOMTopLevelEventType); - const listeners = eventTypeMap.get(type); - if (listeners !== undefined) { - const captureListeners = Array.from(listeners.captured); - const bubbleListeners = Array.from(listeners.bubbled); - - for (let i = 0; i < captureListeners.length; i++) { - const listener = captureListeners[i]; - const {callback} = listener; - dispatchListeners.unshift(callback); - dispatchInstances.unshift(((lastHostComponent: any): Element)); - } - for (let i = 0; i < bubbleListeners.length; i++) { - const listener = bubbleListeners[i]; - const {callback} = listener; - dispatchListeners.push(callback); - dispatchInstances.push(((lastHostComponent: any): Element)); - } - } - } } node = node.return; } diff --git a/packages/react-dom/src/shared/ReactDOMTypes.js b/packages/react-dom/src/shared/ReactDOMTypes.js index 25dc9b0246049..53b21bee8c6ef 100644 --- a/packages/react-dom/src/shared/ReactDOMTypes.js +++ b/packages/react-dom/src/shared/ReactDOMTypes.js @@ -12,7 +12,6 @@ import type { ReactEventResponder, ReactEventResponderInstance, EventPriority, - ReactScopeMethods, } from 'shared/ReactTypes'; import type {DOMTopLevelEventType} from 'legacy-events/TopLevelEventTypes'; @@ -87,15 +86,12 @@ export type ReactDOMListenerEvent = {| export type ReactDOMListenerMap = {| clear: () => void, - setListener: ( - target: EventTarget | ReactScopeMethods, - callback: ?(Event) => void, - ) => void, + setListener: (target: EventTarget, callback: ?(Event) => void) => void, |}; export type ReactDOMListener = {| callback: Event => void, destroy: Node => void, event: ReactDOMListenerEvent, - target: EventTarget | ReactScopeMethods, + target: EventTarget, |}; diff --git a/packages/react-reconciler/src/ReactFiberHooks.js b/packages/react-reconciler/src/ReactFiberHooks.js index 71b9f75575bd1..8eefb9dc2f0a3 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.js +++ b/packages/react-reconciler/src/ReactFiberHooks.js @@ -14,7 +14,6 @@ import type { ReactEventResponder, ReactContext, ReactEventResponderListener, - ReactScopeMethods, } from 'shared/ReactTypes'; import type {Fiber} from './ReactFiber'; import type {ExpirationTime} from './ReactFiberExpirationTime'; @@ -1652,7 +1651,7 @@ function validateNotInFunctionRender(): boolean { function createReactListener( event: ReactListenerEvent, callback: Event => void, - target: EventTarget | ReactScopeMethods, + target: EventTarget, destroy: Node => void, ): ReactListener { return { @@ -1666,10 +1665,7 @@ function createReactListener( function mountEventListener(event: ReactListenerEvent): ReactListenerMap { if (enableUseEventAPI) { const hook = mountWorkInProgressHook(); - const listenerMap: Map< - EventTarget | ReactScopeMethods, - ReactListener, - > = new Map(); + const listenerMap: Map = new Map(); const rootContainerInstance = getRootHostContainer(); // Register the event to the current root to ensure event @@ -1704,10 +1700,7 @@ function mountEventListener(event: ReactListenerEvent): ReactListenerMap { const reactListenerMap: ReactListenerMap = { clear, - setListener( - target: EventTarget | ReactScopeMethods, - callback: ?(Event) => void, - ): void { + setListener(target: EventTarget, callback: ?(Event) => void): void { if ( validateNotInFunctionRender() && validateEventListenerTarget(target, callback)