diff --git a/packages/react-art/src/ReactARTHostConfig.js b/packages/react-art/src/ReactARTHostConfig.js index 8c50b26ecc..d1a4145ff3 100644 --- a/packages/react-art/src/ReactARTHostConfig.js +++ b/packages/react-art/src/ReactARTHostConfig.js @@ -493,22 +493,6 @@ export function makeServerId(): OpaqueIDType { throw new Error('Not yet implemented'); } -export function registerEvent(event: any, rootContainerInstance: any) { - throw new Error('Not yet implemented.'); -} - -export function mountEventListener(listener: any) { - throw new Error('Not yet implemented.'); -} - -export function unmountEventListener(listener: any) { - throw new Error('Not yet implemented.'); -} - -export function validateEventListenerTarget(target: any, listener: any) { - throw new Error('Not yet implemented.'); -} - export function beforeActiveInstanceBlur() { // noop } diff --git a/packages/react-debug-tools/src/ReactDebugHooks.js b/packages/react-debug-tools/src/ReactDebugHooks.js index a65c78174d..c9be0d66f7 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, @@ -48,14 +47,6 @@ type HookLogEntry = { ... }; -type ReactDebugListenerMap = {| - clear: () => void, - setListener: ( - target: EventTarget | ReactScopeMethods, - callback: ?(SyntheticEvent) => void, - ) => void, -|}; - let hookLog: Array = []; // Primitives @@ -311,16 +302,6 @@ function useTransition( return [callback => {}, false]; } -const noOp = () => {}; - -function useEvent(event: any): ReactDebugListenerMap { - hookLog.push({primitive: 'Event', stackError: new Error(), value: event}); - return { - clear: noOp, - setListener: noOp, - }; -} - function useDeferredValue(value: T, config: TimeoutConfig | null | void): T { // useDeferredValue() composes multiple hooks internally. // Advance the current hook index the same number of times @@ -368,7 +349,6 @@ const Dispatcher: DispatcherType = { useTransition, useMutableSource, useDeferredValue, - useEvent, useOpaqueIdentifier, }; diff --git a/packages/react-debug-tools/src/__tests__/ReactHooksInspection-test.internal.js b/packages/react-debug-tools/src/__tests__/ReactHooksInspection-test.internal.js index 63ec249079..d5874c5621 100644 --- a/packages/react-debug-tools/src/__tests__/ReactHooksInspection-test.internal.js +++ b/packages/react-debug-tools/src/__tests__/ReactHooksInspection-test.internal.js @@ -11,7 +11,6 @@ 'use strict'; let React; -let ReactDOM; let ReactDebugTools; describe('ReactHooksInspection', () => { @@ -19,9 +18,7 @@ describe('ReactHooksInspection', () => { jest.resetModules(); const ReactFeatureFlags = require('shared/ReactFeatureFlags'); ReactFeatureFlags.enableDeprecatedFlareAPI = true; - ReactFeatureFlags.enableUseEventAPI = true; React = require('react'); - ReactDOM = require('react-dom'); ReactDebugTools = require('react-debug-tools'); }); @@ -46,45 +43,4 @@ describe('ReactHooksInspection', () => { }, ]); }); - - // @gate experimental - it('should inspect a simple ReactDOM.useEvent hook', () => { - let clickHandle; - let ref; - - const effect = () => { - clickHandle.setListener(ref.current, () => {}); - }; - - function Foo(props) { - ref = React.useRef(null); - clickHandle = ReactDOM.unstable_useEvent('click'); - React.useEffect(effect); - return
Hello world
; - } - const tree = ReactDebugTools.inspectHooks(Foo, {}); - expect(tree).toEqual([ - { - isStateEditable: false, - id: 0, - name: 'Ref', - subHooks: [], - value: null, - }, - { - isStateEditable: false, - id: 1, - name: 'Event', - value: {capture: false, passive: undefined, priority: 0, type: 'click'}, - subHooks: [], - }, - { - isStateEditable: false, - id: 2, - name: 'Effect', - value: effect, - subHooks: [], - }, - ]); - }); }); diff --git a/packages/react-dom/index.classic.fb.js b/packages/react-dom/index.classic.fb.js index 4d6939900b..e946df7b76 100644 --- a/packages/react-dom/index.classic.fb.js +++ b/packages/react-dom/index.classic.fb.js @@ -36,5 +36,4 @@ export { unstable_scheduleHydration, unstable_renderSubtreeIntoContainer, unstable_createPortal, - unstable_useEvent, } from './src/client/ReactDOM'; diff --git a/packages/react-dom/index.modern.fb.js b/packages/react-dom/index.modern.fb.js index d8da74a5df..bcb213ad4e 100644 --- a/packages/react-dom/index.modern.fb.js +++ b/packages/react-dom/index.modern.fb.js @@ -19,5 +19,4 @@ export { unstable_flushDiscreteUpdates, unstable_flushControlled, unstable_scheduleHydration, - unstable_useEvent, } from './src/client/ReactDOM'; diff --git a/packages/react-dom/src/client/ReactDOM.js b/packages/react-dom/src/client/ReactDOM.js index 600edb3cce..ee294218ce 100644 --- a/packages/react-dom/src/client/ReactDOM.js +++ b/packages/react-dom/src/client/ReactDOM.js @@ -20,7 +20,6 @@ import { unmountComponentAtNode, } from './ReactDOMLegacy'; import {createRoot, createBlockingRoot, isValidContainer} from './ReactDOMRoot'; -import {useEvent} from './ReactDOMUseEvent'; import { batchedEventUpdates, @@ -211,8 +210,6 @@ export { // Temporary alias since we already shipped React 16 RC with it. // TODO: remove in React 17. unstable_createPortal, - // enableUseEventAPI - useEvent as unstable_useEvent, }; const foundDevTools = injectIntoDevTools({ diff --git a/packages/react-dom/src/client/ReactDOMComponentTree.js b/packages/react-dom/src/client/ReactDOMComponentTree.js index be0c8600dc..c8c6bfc0df 100644 --- a/packages/react-dom/src/client/ReactDOMComponentTree.js +++ b/packages/react-dom/src/client/ReactDOMComponentTree.js @@ -15,7 +15,6 @@ import type { SuspenseInstance, Props, } from './ReactDOMHostConfig'; -import type {ReactDOMListener} from '../shared/ReactDOMTypes'; import { HostComponent, @@ -33,7 +32,6 @@ const randomKey = Math.random() const internalInstanceKey = '__reactFiber$' + randomKey; const internalEventHandlersKey = '__reactEvents$' + randomKey; const internalContainerInstanceKey = '__reactContainer$' + randomKey; -const internalEventListenersKey = '__reactListeners$' + randomKey; export function precacheFiberNode( hostInst: Fiber, @@ -187,18 +185,3 @@ export function updateFiberProps( ): void { (node: any)[internalEventHandlersKey] = props; } - -// This is used for useEvent listeners -export function getListenersFromTarget( - target: EventTarget, -): null | Set { - return (target: any)[internalEventListenersKey] || null; -} - -// This is used for useEvent listeners -export function initListenersSet( - target: EventTarget, - value: Set, -): void { - (target: any)[internalEventListenersKey] = value; -} diff --git a/packages/react-dom/src/client/ReactDOMHostConfig.js b/packages/react-dom/src/client/ReactDOMHostConfig.js index 70f23dba42..15a317b1f6 100644 --- a/packages/react-dom/src/client/ReactDOMHostConfig.js +++ b/packages/react-dom/src/client/ReactDOMHostConfig.js @@ -13,17 +13,12 @@ import type { ReactDOMEventResponder, ReactDOMEventResponderInstance, ReactDOMFundamentalComponentInstance, - ReactDOMListener, - ReactDOMListenerEvent, - ReactDOMListenerMap, } from '../shared/ReactDOMTypes'; -import type {ReactScopeMethods} from 'shared/ReactTypes'; import { precacheFiberNode, updateFiberProps, getClosestInstanceFromNode, - getListenersFromTarget, } from './ReactDOMComponentTree'; import { createElement, @@ -69,32 +64,9 @@ import { enableSuspenseServerRenderer, enableDeprecatedFlareAPI, enableFundamentalAPI, - enableUseEventAPI, - enableScopeAPI, } from 'shared/ReactFeatureFlags'; -import { - PLUGIN_EVENT_SYSTEM, - USE_EVENT_SYSTEM, -} from '../events/EventSystemFlags'; -import { - isManagedDOMElement, - isValidEventTarget, - listenToTopLevelEvent, - attachListenerToManagedDOMElement, - detachListenerFromManagedDOMElement, - attachTargetEventListener, - detachTargetEventListener, - isReactScope, - attachListenerToReactScope, - detachListenerFromReactScope, -} from '../events/DOMModernPluginEventSystem'; -import {getListenerMapForElement} from '../events/DOMEventListenerMap'; import {TOP_BEFORE_BLUR, TOP_AFTER_BLUR} from '../events/DOMTopLevelEventTypes'; -export type ReactListenerEvent = ReactDOMListenerEvent; -export type ReactListenerMap = ReactDOMListenerMap; -export type ReactListener = ReactDOMListener; - export type Type = string; export type Props = { autoFocus?: boolean, @@ -246,7 +218,7 @@ export function prepareForCommit(containerInfo: Container): Object | null { eventsEnabled = ReactBrowserEventEmitterIsEnabled(); selectionInformation = getSelectionInformation(); let activeInstance = null; - if (enableDeprecatedFlareAPI || enableUseEventAPI) { + if (enableDeprecatedFlareAPI) { const focusedElem = selectionInformation.focusedElem; if (focusedElem !== null) { activeInstance = getClosestInstanceFromNode(focusedElem); @@ -257,7 +229,7 @@ export function prepareForCommit(containerInfo: Container): Object | null { } export function beforeActiveInstanceBlur(): void { - if (enableDeprecatedFlareAPI || enableUseEventAPI) { + if (enableDeprecatedFlareAPI) { ReactBrowserEventEmitterSetEnabled(true); dispatchBeforeDetachedBlur((selectionInformation: any).focusedElem); ReactBrowserEventEmitterSetEnabled(false); @@ -265,7 +237,7 @@ export function beforeActiveInstanceBlur(): void { } export function afterActiveInstanceBlur(): void { - if (enableDeprecatedFlareAPI || enableUseEventAPI) { + if (enableDeprecatedFlareAPI) { ReactBrowserEventEmitterSetEnabled(true); dispatchAfterDetachedBlur((selectionInformation: any).focusedElem); ReactBrowserEventEmitterSetEnabled(false); @@ -528,7 +500,7 @@ function createEvent(type: TopLevelType): Event { } function dispatchBeforeDetachedBlur(target: HTMLElement): void { - if (enableDeprecatedFlareAPI || enableUseEventAPI) { + if (enableDeprecatedFlareAPI) { const event = createEvent(TOP_BEFORE_BLUR); // Dispatch "beforeblur" directly on the target, // so it gets picked up by the event system and @@ -538,7 +510,7 @@ function dispatchBeforeDetachedBlur(target: HTMLElement): void { } function dispatchAfterDetachedBlur(target: HTMLElement): void { - if (enableDeprecatedFlareAPI || enableUseEventAPI) { + if (enableDeprecatedFlareAPI) { const event = createEvent(TOP_AFTER_BLUR); // So we know what was detached, make the relatedTarget the // detached target on the "afterblur" event. @@ -550,23 +522,8 @@ function dispatchAfterDetachedBlur(target: HTMLElement): void { export function beforeRemoveInstance( instance: Instance | TextInstance | SuspenseInstance, -): void { - if (enableUseEventAPI) { - // It's unfortunate that we have to do this cleanup, but - // it's necessary otherwise we will leak the host instances - // from the useEvent hook instances Map. We call destroy - // on each listener to ensure we properly remove the instance - // from the instances Map. Note: we have this Map so that we - // can properly unmount instances when the function component - // that the hook is attached to gets unmounted. - const listenersSet = getListenersFromTarget(instance); - if (listenersSet !== null) { - const listeners = Array.from(listenersSet); - for (let i = 0; i < listeners.length; i++) { - listeners[i].destroy(instance); - } - } - } +) { + // TODO for ReactDOM.createEventInstance } export function removeChild( @@ -1141,78 +1098,3 @@ export function makeOpaqueHydratingObject( valueOf: attemptToReadValue, }; } - -export function registerEvent( - event: ReactDOMListenerEvent, - rootContainerInstance: Container, -): void { - const {passive, priority, type} = event; - const listenerMap = getListenerMapForElement(rootContainerInstance); - // Add the event listener to the target container (falling back to - // the target if we didn't find one). - listenToTopLevelEvent( - type, - rootContainerInstance, - listenerMap, - PLUGIN_EVENT_SYSTEM | USE_EVENT_SYSTEM, - passive, - priority, - ); -} - -export function mountEventListener(listener: ReactDOMListener): void { - if (enableUseEventAPI) { - const {target} = listener; - if (isManagedDOMElement(target)) { - attachListenerToManagedDOMElement(listener); - } else if (enableScopeAPI && isReactScope(target)) { - attachListenerToReactScope(listener); - } else { - attachTargetEventListener(listener); - } - } -} - -export function unmountEventListener(listener: ReactDOMListener): void { - if (enableUseEventAPI) { - const {target} = listener; - if (isManagedDOMElement(target)) { - detachListenerFromManagedDOMElement(listener); - } else if (enableScopeAPI && isReactScope(target)) { - detachListenerFromReactScope(listener); - } else { - detachTargetEventListener(listener); - } - } -} - -export function validateEventListenerTarget( - target: EventTarget | ReactScopeMethods, - listener: ?(SyntheticEvent) => void, -): boolean { - if (enableUseEventAPI) { - if ( - target != null && - (isManagedDOMElement(target) || - isValidEventTarget(target) || - isReactScope(target)) - ) { - if (listener == null || typeof listener === 'function') { - return true; - } - if (__DEV__) { - console.warn( - 'Event listener method setListener() from useEvent() hook requires the second argument' + - ' to be either a valid function callback or null/undefined.', - ); - } - } - if (__DEV__) { - console.warn( - 'Event listener method setListener() from useEvent() hook requires the first argument to be ' + - 'a valid DOM EventTarget. If using a ref, ensure the current value is not null.', - ); - } - } - return false; -} diff --git a/packages/react-dom/src/client/ReactDOMUseEvent.js b/packages/react-dom/src/client/ReactDOMUseEvent.js deleted file mode 100644 index f1c3145c06..0000000000 --- a/packages/react-dom/src/client/ReactDOMUseEvent.js +++ /dev/null @@ -1,79 +0,0 @@ -/** - * 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 type {DOMTopLevelEventType} from 'legacy-events/TopLevelEventTypes'; -import type {EventPriority} from 'shared/ReactTypes'; -import type { - ReactDOMListenerEvent, - ReactDOMListenerMap, -} from '../shared/ReactDOMTypes'; - -import ReactSharedInternals from 'shared/ReactSharedInternals'; -import invariant from 'shared/invariant'; - -import {getEventPriorityForListenerSystem} from '../events/DOMEventProperties'; - -type EventOptions = {| - capture?: boolean, - passive?: boolean, - priority?: EventPriority, -|}; - -const {ReactCurrentDispatcher} = ReactSharedInternals; - -function resolveDispatcher() { - const dispatcher = ReactCurrentDispatcher.current; - invariant( - dispatcher !== null, - 'Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for' + - ' one of the following reasons:\n' + - '1. You might have mismatching versions of React and the renderer (such as React DOM)\n' + - '2. You might be breaking the Rules of Hooks\n' + - '3. You might have more than one copy of React in the same app\n' + - 'See https://fb.me/react-invalid-hook-call for tips about how to debug and fix this problem.', - ); - return dispatcher; -} - -export function useEvent( - type: string, - options?: EventOptions, -): ReactDOMListenerMap { - const topLevelType = ((type: any): DOMTopLevelEventType); - const dispatcher = resolveDispatcher(); - let capture = false; - let passive = undefined; // Undefined means to use the browser default - let priority; - - if (options != null) { - const optionsCapture = options.capture; - const optionsPassive = options.passive; - const optionsPriority = options.priority; - - if (typeof optionsCapture === 'boolean') { - capture = optionsCapture; - } - if (typeof optionsPassive === 'boolean') { - passive = optionsPassive; - } - if (typeof optionsPriority === 'number') { - priority = optionsPriority; - } - } - if (priority === undefined) { - priority = getEventPriorityForListenerSystem(topLevelType); - } - const event: ReactDOMListenerEvent = { - capture, - passive, - priority, - type: topLevelType, - }; - return dispatcher.useEvent(event); -} diff --git a/packages/react-dom/src/events/DOMEventProperties.js b/packages/react-dom/src/events/DOMEventProperties.js index d30fc8f991..6a3b0e68fb 100644 --- a/packages/react-dom/src/events/DOMEventProperties.js +++ b/packages/react-dom/src/events/DOMEventProperties.js @@ -23,7 +23,6 @@ import { UserBlockingEvent, ContinuousEvent, } from 'shared/ReactTypes'; -import {enableUseEventAPI} from 'shared/ReactFeatureFlags'; // Needed for SimpleEventPlugin, rather than // do it in two places, which duplicates logic @@ -96,13 +95,6 @@ const otherDiscreteEvents = [ DOMTopLevelEventTypes.TOP_COMPOSITION_UPDATE, ]; -if (enableUseEventAPI) { - otherDiscreteEvents.push( - DOMTopLevelEventTypes.TOP_BEFORE_BLUR, - DOMTopLevelEventTypes.TOP_AFTER_BLUR, - ); -} - // prettier-ignore const userBlockingPairsForSimpleEventPlugin = [ DOMTopLevelEventTypes.TOP_DRAG, 'drag', diff --git a/packages/react-dom/src/events/DOMModernPluginEventSystem.js b/packages/react-dom/src/events/DOMModernPluginEventSystem.js index e2d4084e15..c347e718bb 100644 --- a/packages/react-dom/src/events/DOMModernPluginEventSystem.js +++ b/packages/react-dom/src/events/DOMModernPluginEventSystem.js @@ -17,11 +17,7 @@ import type {EventSystemFlags} from './EventSystemFlags'; import type {EventPriority, ReactScopeMethods} from 'shared/ReactTypes'; import type {Fiber} from 'react-reconciler/src/ReactInternalTypes'; import type {PluginModule} from 'legacy-events/PluginModuleType'; -import type { - ReactSyntheticEvent, - CustomDispatchConfig, -} from 'legacy-events/ReactSyntheticEventType'; -import type {ReactDOMListener} from '../shared/ReactDOMTypes'; +import type {ReactSyntheticEvent} from 'legacy-events/ReactSyntheticEventType'; import {registrationNameDependencies} from 'legacy-events/EventPluginRegistry'; import {plugins} from 'legacy-events/EventPluginRegistry'; @@ -36,7 +32,6 @@ import { import { HostRoot, HostPortal, - ScopeComponent, HostComponent, } from 'react-reconciler/src/ReactWorkTags'; @@ -80,25 +75,14 @@ import { TOP_PROGRESS, TOP_PLAYING, TOP_CLICK, - TOP_BEFORE_BLUR, - TOP_AFTER_BLUR, TOP_SELECTION_CHANGE, } from './DOMTopLevelEventTypes'; -import { - getClosestInstanceFromNode, - getListenersFromTarget, - initListenersSet, -} from '../client/ReactDOMComponentTree'; +import {getClosestInstanceFromNode} from '../client/ReactDOMComponentTree'; import {COMMENT_NODE} from '../shared/HTMLNodeType'; -import {topLevelEventsToDispatchConfig} from './DOMEventProperties'; import {batchedEventUpdates} from './ReactDOMUpdateBatching'; import getListener from './getListener'; -import { - enableLegacyFBSupport, - enableUseEventAPI, - enableScopeAPI, -} from 'shared/ReactFeatureFlags'; +import {enableLegacyFBSupport} from 'shared/ReactFeatureFlags'; import { invokeGuardedCallbackAndCatchFirstError, rethrowCaughtError, @@ -140,44 +124,8 @@ const capturePhaseEvents = new Set([ TOP_WAITING, ]); -if (enableUseEventAPI) { - capturePhaseEvents.add(TOP_BEFORE_BLUR); - capturePhaseEvents.add(TOP_AFTER_BLUR); -} - -const emptyDispatchConfigForCustomEvents: CustomDispatchConfig = { - customEvent: true, - phasedRegistrationNames: { - bubbled: null, - captured: null, - }, -}; - 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 cannot handle polymorphic WeakMaps -const eventTargetEventListenerStore: WeakMap< - EventTarget, - Map< - DOMTopLevelEventType, - {bubbled: Set, captured: Set}, - >, -> = new PossiblyWeakMap(); - -// $FlowFixMe: Flow cannot handle polymorphic WeakMaps -const reactScopeListenerStore: WeakMap< - ReactScopeMethods, - Map< - DOMTopLevelEventType, - {bubbled: Set, captured: Set}, - >, -> = new PossiblyWeakMap(); - function executeDispatch( event: ReactSyntheticEvent, listener: Function, @@ -514,183 +462,7 @@ export function dispatchEventForPluginEventSystem( ); } -function getNearestRootOrPortalContainer(instance: Element): Element { - let node = getClosestInstanceFromNode(instance); - while (node !== null) { - const tag = node.tag; - // Once we encounter a host container or root container - // we can return their DOM instance. - if (tag === HostRoot || tag === HostPortal) { - return node.stateNode.containerInfo; - } - node = node.return; - } - return instance; -} - -function addEventTypeToDispatchConfig(type: DOMTopLevelEventType): void { - const dispatchConfig = topLevelEventsToDispatchConfig.get(type); - // If we don't have a dispatchConfig, then we're dealing with - // an event type that React does not know about (i.e. a custom event). - // We need to register an event config for this or the SimpleEventPlugin - // will not appropriately provide a SyntheticEvent, so we use out empty - // dispatch config for custom events. - if (dispatchConfig === undefined) { - topLevelEventsToDispatchConfig.set( - type, - emptyDispatchConfigForCustomEvents, - ); - } -} - -export function attachListenerToManagedDOMElement( - listener: ReactDOMListener, -): void { - const {event, target} = listener; - const {passive, priority, type} = event; - - const managedTargetElement = ((target: any): Element); - const containerEventTarget = getNearestRootOrPortalContainer( - managedTargetElement, - ); - const listenerMap = getListenerMapForElement(containerEventTarget); - // Add the event listener to the target container (falling back to - // the target if we didn't find one). - listenToTopLevelEvent( - type, - containerEventTarget, - listenerMap, - PLUGIN_EVENT_SYSTEM | USE_EVENT_SYSTEM, - passive, - priority, - ); - // Get the internal listeners Set from the target instance. - let listeners = getListenersFromTarget(managedTargetElement); - // If we don't have any listeners, then we need to init them. - if (listeners === null) { - listeners = new Set(); - initListenersSet(managedTargetElement, listeners); - } - // Add our listener to the listeners Set. - listeners.add(listener); - // Finally, add the event to our known event types list. - addEventTypeToDispatchConfig(type); -} - -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); - if (listeners !== null) { - // Remove out listener from the listeners Set. - listeners.delete(listener); - } -} - -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); - // Add the event listener to the TargetEvent object. - listenToTopLevelEvent( - type, - eventTarget, - listenerMap, - PLUGIN_EVENT_SYSTEM | USE_EVENT_SYSTEM | IS_TARGET_PHASE_ONLY, - passive, - priority, - capture, - ); - let eventTypeMap = eventTargetEventListenerStore.get(eventTarget); - if (eventTypeMap === undefined) { - eventTypeMap = new Map(); - eventTargetEventListenerStore.set(eventTarget, 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 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); - 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 accumulateTwoPhaseListeners( - event: ReactSyntheticEvent, - accumulateUseEventListeners?: boolean, -): void { +export function accumulateTwoPhaseListeners(event: ReactSyntheticEvent): void { const phasedRegistrationNames = event.dispatchConfig.phasedRegistrationNames; const dispatchListeners = []; const dispatchInstances: Array = []; @@ -701,7 +473,6 @@ export function accumulateTwoPhaseListeners( // usual two phase accumulation using the React fiber tree to pick up // all relevant useEvent and on* prop events. let instance = event._targetInst; - let lastHostComponent = null; // Accumulate all instances and listeners via the target -> root path. while (instance !== null) { @@ -709,35 +480,6 @@ export function accumulateTwoPhaseListeners( // Handle listeners that are on HostComponents (i.e.
) if (tag === HostComponent && stateNode !== null) { const currentTarget = stateNode; - lastHostComponent = currentTarget; - // For useEvent listenrs - if (enableUseEventAPI && accumulateUseEventListeners) { - // useEvent event listeners - const targetType = event.type; - const listeners = getListenersFromTarget(currentTarget); - - if (listeners !== null) { - const listenersArr = Array.from(listeners); - for (let i = 0; i < listenersArr.length; i++) { - const listener = listenersArr[i]; - const { - callback, - event: {capture, type}, - } = listener; - if (type === targetType) { - if (capture === true) { - dispatchListeners.unshift(callback); - dispatchInstances.unshift(instance); - dispatchCurrentTargets.unshift(currentTarget); - } else { - dispatchListeners.push(callback); - dispatchInstances.push(instance); - dispatchCurrentTargets.push(currentTarget); - } - } - } - } - } // Standard React on* listeners, i.e. onClick prop if (captured !== null) { const captureListener = getListener(instance, captured); @@ -760,40 +502,6 @@ export function accumulateTwoPhaseListeners( } } } - if ( - enableUseEventAPI && - enableScopeAPI && - accumulateUseEventListeners && - tag === ScopeComponent && - lastHostComponent !== null - ) { - const reactScope = 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); - const lastCurrentTarget = ((lastHostComponent: any): Element); - - for (let i = 0; i < captureListeners.length; i++) { - const listener = captureListeners[i]; - const {callback} = listener; - dispatchListeners.unshift(callback); - dispatchInstances.unshift(instance); - dispatchCurrentTargets.unshift(lastCurrentTarget); - } - for (let i = 0; i < bubbleListeners.length; i++) { - const listener = bubbleListeners[i]; - const {callback} = listener; - dispatchListeners.push(callback); - dispatchInstances.push(instance); - dispatchCurrentTargets.push(lastCurrentTarget); - } - } - } - } instance = instance.return; } @@ -806,57 +514,6 @@ export function accumulateTwoPhaseListeners( } } -export function accumulateEventTargetListeners( - event: ReactSyntheticEvent, - currentTarget: EventTarget, -): void { - const dispatchListeners = []; - const dispatchInstances: Array = []; - const dispatchCurrentTargets = []; - - const eventTypeMap = eventTargetEventListenerStore.get(currentTarget); - if (eventTypeMap !== undefined) { - const type = ((event.type: any): DOMTopLevelEventType); - const listeners = eventTypeMap.get(type); - if (listeners !== undefined) { - const isCapturePhase = (event: any).eventPhase === 1; - - if (isCapturePhase) { - const captureListeners = Array.from(listeners.captured); - - for (let i = captureListeners.length - 1; i >= 0; i--) { - const listener = captureListeners[i]; - const {callback} = listener; - dispatchListeners.push(callback); - // EventTarget listeners do not have instances, as there - // is no backing Fiber instance for them (window, document etc). - dispatchInstances.push(null); - dispatchCurrentTargets.push(currentTarget); - } - } else { - const bubbleListeners = Array.from(listeners.bubbled); - - for (let i = 0; i < bubbleListeners.length; i++) { - const listener = bubbleListeners[i]; - const {callback} = listener; - dispatchListeners.push(callback); - // EventTarget listeners do not have instances, as there - // is no backing Fiber instance for them (window, document etc). - dispatchInstances.push(null); - dispatchCurrentTargets.push(currentTarget); - } - } - } - } - // To prevent allocation to the event unless we actually - // have listeners we check the length of one of the arrays. - if (dispatchListeners.length > 0) { - event._dispatchListeners = dispatchListeners; - event._dispatchInstances = dispatchInstances; - event._dispatchCurrentTargets = dispatchCurrentTargets; - } -} - function getParent(inst: Fiber | null): Fiber | null { if (inst === null) { return null; diff --git a/packages/react-dom/src/events/ReactDOMEventListener.js b/packages/react-dom/src/events/ReactDOMEventListener.js index 46c8b26c7a..44a0a65b23 100644 --- a/packages/react-dom/src/events/ReactDOMEventListener.js +++ b/packages/react-dom/src/events/ReactDOMEventListener.js @@ -44,7 +44,6 @@ import { addEventBubbleListener, addEventCaptureListener, addEventCaptureListenerWithPassiveFlag, - addEventBubbleListenerWithPassiveFlag, removeEventListener, } from './EventListener'; import getEventTarget from './getEventTarget'; @@ -56,7 +55,6 @@ import { enableDeprecatedFlareAPI, enableModernEventSystem, enableLegacyFBSupport, - enableUseEventAPI, } from 'shared/ReactFeatureFlags'; import { UserBlockingEvent, @@ -200,37 +198,17 @@ export function addTrappedEventListener( }; } if (capture) { - if (enableUseEventAPI && passive !== undefined) { - // This is only used with passive is either true or false. - unsubscribeListener = addEventCaptureListenerWithPassiveFlag( - targetContainer, - rawEventName, - listener, - passive, - ); - } else { - unsubscribeListener = addEventCaptureListener( - targetContainer, - rawEventName, - listener, - ); - } + unsubscribeListener = addEventCaptureListener( + targetContainer, + rawEventName, + listener, + ); } else { - if (enableUseEventAPI && passive !== undefined) { - // This is only used with passive is either true or false. - unsubscribeListener = addEventBubbleListenerWithPassiveFlag( - targetContainer, - rawEventName, - listener, - passive, - ); - } else { - unsubscribeListener = addEventBubbleListener( - targetContainer, - rawEventName, - listener, - ); - } + unsubscribeListener = addEventBubbleListener( + targetContainer, + rawEventName, + listener, + ); } return unsubscribeListener; } 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 f73c32580d..538353fff4 100644 --- a/packages/react-dom/src/events/__tests__/DOMModernPluginEventSystem-test.internal.js +++ b/packages/react-dom/src/events/__tests__/DOMModernPluginEventSystem-test.internal.js @@ -9,13 +9,10 @@ 'use strict'; -import {createEventTarget} from 'dom-event-testing-library'; - let React; let ReactFeatureFlags; let ReactDOM; let ReactDOMServer; -let ReactTestUtils; let Scheduler; function dispatchEvent(element, type) { @@ -67,7 +64,6 @@ describe('DOMModernPluginEventSystem', () => { ReactDOM = require('react-dom'); Scheduler = require('scheduler'); ReactDOMServer = require('react-dom/server'); - ReactTestUtils = require('react-dom/test-utils'); container = document.createElement('div'); document.body.appendChild(container); startNativeEventListenerClearDown(); @@ -1082,1551 +1078,6 @@ describe('DOMModernPluginEventSystem', () => { ]); } }); - - describe('ReactDOM.useEvent', () => { - beforeEach(() => { - jest.resetModules(); - ReactFeatureFlags = require('shared/ReactFeatureFlags'); - ReactFeatureFlags.enableModernEventSystem = true; - ReactFeatureFlags.enableUseEventAPI = true; - - React = require('react'); - ReactDOM = require('react-dom'); - Scheduler = require('scheduler'); - ReactDOMServer = require('react-dom/server'); - ReactTestUtils = require('react-dom/test-utils'); - }); - - // @gate experimental - it('should create the same event listener map', () => { - const listenerMaps = []; - - function Test() { - const listenerMap = ReactDOM.unstable_useEvent('click'); - - listenerMaps.push(listenerMap); - - return
; - } - - ReactDOM.render(, container); - ReactDOM.render(, container); - expect(listenerMaps.length).toEqual(2); - expect(listenerMaps[0]).toEqual(listenerMaps[1]); - }); - - // @gate experimental - it('can render correctly with the ReactDOMServer', () => { - const clickEvent = jest.fn(); - - function Test() { - const divRef = React.useRef(null); - const click = ReactDOM.unstable_useEvent('click'); - - React.useEffect(() => { - click.setListener(divRef.current, clickEvent); - }); - - return
Hello world
; - } - const output = ReactDOMServer.renderToString(); - expect(output).toBe(`
Hello world
`); - }); - - // @gate experimental - it('can render correctly with the ReactDOMServer hydration', () => { - const clickEvent = jest.fn(); - const spanRef = React.createRef(); - - function Test() { - const click = ReactDOM.unstable_useEvent('click'); - - React.useEffect(() => { - click.setListener(spanRef.current, clickEvent); - }); - - return ( -
- Hello world -
- ); - } - const output = ReactDOMServer.renderToString(); - expect(output).toBe( - `
Hello world
`, - ); - container.innerHTML = output; - ReactDOM.hydrate(, container); - Scheduler.unstable_flushAll(); - dispatchClickEvent(spanRef.current); - expect(clickEvent).toHaveBeenCalledTimes(1); - }); - - // @gate experimental - it('should correctly work for a basic "click" listener', () => { - let log = []; - const clickEvent = jest.fn(event => { - log.push({ - eventPhase: event.eventPhase, - type: event.type, - currentTarget: event.currentTarget, - target: event.target, - }); - }); - const divRef = React.createRef(); - const buttonRef = React.createRef(); - - function Test() { - const click = ReactDOM.unstable_useEvent('click'); - - React.useEffect(() => { - click.setListener(buttonRef.current, clickEvent); - }); - - return ( - - ); - } - - ReactDOM.render(, container); - Scheduler.unstable_flushAll(); - - expect(container.innerHTML).toBe( - '', - ); - - // Clicking the button should trigger the event callback - let divElement = divRef.current; - dispatchClickEvent(divElement); - expect(log).toEqual([ - { - eventPhase: 3, - type: 'click', - currentTarget: buttonRef.current, - target: divRef.current, - }, - ]); - expect(clickEvent).toBeCalledTimes(1); - - // Unmounting the container and clicking should not work - ReactDOM.render(null, container); - Scheduler.unstable_flushAll(); - - dispatchClickEvent(divElement); - expect(clickEvent).toBeCalledTimes(1); - - // Re-rendering the container and clicking should work - ReactDOM.render(, container); - Scheduler.unstable_flushAll(); - - divElement = divRef.current; - dispatchClickEvent(divElement); - expect(clickEvent).toBeCalledTimes(2); - - log = []; - - // Clicking the button should also work - const buttonElement = buttonRef.current; - dispatchClickEvent(buttonElement); - expect(log).toEqual([ - { - eventPhase: 3, - type: 'click', - currentTarget: buttonRef.current, - target: buttonRef.current, - }, - ]); - - function Test2({clickEvent2}) { - const click = ReactDOM.unstable_useEvent('click', clickEvent2); - - React.useEffect(() => { - click.setListener(buttonRef.current, clickEvent2); - }); - - return ( - - ); - } - - let clickEvent2 = jest.fn(); - ReactDOM.render(, container); - Scheduler.unstable_flushAll(); - - divElement = divRef.current; - dispatchClickEvent(divElement); - expect(clickEvent2).toBeCalledTimes(1); - - // Reset the function we pass in, so it's different - clickEvent2 = jest.fn(); - ReactDOM.render(, container); - Scheduler.unstable_flushAll(); - - divElement = divRef.current; - dispatchClickEvent(divElement); - expect(clickEvent2).toBeCalledTimes(1); - }); - - // @gate experimental - it('should correctly work for setting and clearing a basic "click" listener', () => { - const clickEvent = jest.fn(); - const divRef = React.createRef(); - const buttonRef = React.createRef(); - - function Test({off}) { - const click = ReactDOM.unstable_useEvent('click'); - - React.useEffect(() => { - click.setListener(buttonRef.current, clickEvent); - }); - - React.useEffect(() => { - if (off) { - click.setListener(buttonRef.current, null); - } - }, [off]); - - return ( - - ); - } - - ReactDOM.render(, container); - Scheduler.unstable_flushAll(); - - let divElement = divRef.current; - dispatchClickEvent(divElement); - expect(clickEvent).toBeCalledTimes(1); - - // The listener should get unmounted in the second effect - ReactDOM.render(, container); - Scheduler.unstable_flushAll(); - - clickEvent.mockClear(); - - divElement = divRef.current; - dispatchClickEvent(divElement); - expect(clickEvent).toBeCalledTimes(0); - }); - - // @gate experimental - it('should handle the target being a text node', () => { - const clickEvent = jest.fn(); - const buttonRef = React.createRef(); - - function Test() { - const click = ReactDOM.unstable_useEvent('click'); - - React.useEffect(() => { - click.setListener(buttonRef.current, clickEvent); - }); - - return ; - } - - ReactDOM.render(, container); - Scheduler.unstable_flushAll(); - - const textNode = buttonRef.current.firstChild; - dispatchClickEvent(textNode); - expect(clickEvent).toBeCalledTimes(1); - }); - - // @gate experimental - it('handle propagation of click events', () => { - const buttonRef = React.createRef(); - const divRef = React.createRef(); - const log = []; - const onClick = jest.fn(e => log.push(['bubble', e.currentTarget])); - const onClickCapture = jest.fn(e => - log.push(['capture', e.currentTarget]), - ); - - function Test() { - const click = ReactDOM.unstable_useEvent('click'); - const clickCapture = ReactDOM.unstable_useEvent('click', { - capture: true, - }); - - React.useEffect(() => { - click.setListener(buttonRef.current, onClick); - clickCapture.setListener(buttonRef.current, onClickCapture); - click.setListener(divRef.current, onClick); - clickCapture.setListener(divRef.current, onClickCapture); - }); - - return ( - - ); - } - - ReactDOM.render(, container); - Scheduler.unstable_flushAll(); - - const buttonElement = buttonRef.current; - dispatchClickEvent(buttonElement); - expect(onClick).toHaveBeenCalledTimes(1); - expect(onClickCapture).toHaveBeenCalledTimes(1); - expect(log[0]).toEqual(['capture', buttonElement]); - expect(log[1]).toEqual(['bubble', buttonElement]); - - log.length = 0; - onClick.mockClear(); - onClickCapture.mockClear(); - - const divElement = divRef.current; - dispatchClickEvent(divElement); - expect(onClick).toHaveBeenCalledTimes(2); - expect(onClickCapture).toHaveBeenCalledTimes(2); - expect(log[0]).toEqual(['capture', buttonElement]); - expect(log[1]).toEqual(['capture', divElement]); - expect(log[2]).toEqual(['bubble', divElement]); - expect(log[3]).toEqual(['bubble', buttonElement]); - }); - - // @gate experimental - it('handle propagation of click events mixed with onClick events', () => { - const buttonRef = React.createRef(); - const divRef = React.createRef(); - const log = []; - const onClick = jest.fn(e => log.push(['bubble', e.currentTarget])); - const onClickCapture = jest.fn(e => - log.push(['capture', e.currentTarget]), - ); - - function Test() { - const click = ReactDOM.unstable_useEvent('click'); - const clickCapture = ReactDOM.unstable_useEvent('click', { - capture: true, - }); - - React.useEffect(() => { - click.setListener(buttonRef.current, onClick); - clickCapture.setListener(buttonRef.current, onClickCapture); - }); - - return ( - - ); - } - - ReactDOM.render(, container); - Scheduler.unstable_flushAll(); - - const buttonElement = buttonRef.current; - dispatchClickEvent(buttonElement); - expect(onClick).toHaveBeenCalledTimes(1); - expect(onClickCapture).toHaveBeenCalledTimes(1); - expect(log[0]).toEqual(['capture', buttonElement]); - expect(log[1]).toEqual(['bubble', buttonElement]); - - const divElement = divRef.current; - dispatchClickEvent(divElement); - expect(onClick).toHaveBeenCalledTimes(3); - expect(onClickCapture).toHaveBeenCalledTimes(3); - expect(log[2]).toEqual(['capture', buttonElement]); - expect(log[3]).toEqual(['capture', divElement]); - expect(log[4]).toEqual(['bubble', divElement]); - expect(log[5]).toEqual(['bubble', buttonElement]); - }); - - // @gate experimental - it('should correctly work for a basic "click" listener on the outer target', () => { - const log = []; - const clickEvent = jest.fn(event => { - log.push({ - eventPhase: event.eventPhase, - type: event.type, - currentTarget: event.currentTarget, - target: event.target, - }); - }); - const divRef = React.createRef(); - const buttonRef = React.createRef(); - - function Test() { - const click = ReactDOM.unstable_useEvent('click'); - - React.useEffect(() => { - click.setListener(divRef.current, clickEvent); - }); - - return ( - - ); - } - - ReactDOM.render(, container); - Scheduler.unstable_flushAll(); - - expect(container.innerHTML).toBe( - '', - ); - - // Clicking the button should trigger the event callback - let divElement = divRef.current; - dispatchClickEvent(divElement); - expect(log).toEqual([ - { - eventPhase: 3, - type: 'click', - currentTarget: divRef.current, - target: divRef.current, - }, - ]); - - // Unmounting the container and clicking should not work - ReactDOM.render(null, container); - dispatchClickEvent(divElement); - expect(clickEvent).toBeCalledTimes(1); - - // Re-rendering the container and clicking should work - ReactDOM.render(, container); - Scheduler.unstable_flushAll(); - - divElement = divRef.current; - dispatchClickEvent(divElement); - expect(clickEvent).toBeCalledTimes(2); - - // Clicking the button should not work - const buttonElement = buttonRef.current; - dispatchClickEvent(buttonElement); - expect(clickEvent).toBeCalledTimes(2); - }); - - // @gate experimental - it('should correctly handle many nested target listeners', () => { - const buttonRef = React.createRef(); - const targetListener1 = jest.fn(); - const targetListener2 = jest.fn(); - const targetListener3 = jest.fn(); - const targetListener4 = jest.fn(); - - function Test() { - const click1 = ReactDOM.unstable_useEvent('click', { - capture: true, - }); - const click2 = ReactDOM.unstable_useEvent('click', { - capture: true, - }); - const click3 = ReactDOM.unstable_useEvent('click'); - const click4 = ReactDOM.unstable_useEvent('click'); - - React.useEffect(() => { - click1.setListener(buttonRef.current, targetListener1); - click2.setListener(buttonRef.current, targetListener2); - click3.setListener(buttonRef.current, targetListener3); - click4.setListener(buttonRef.current, targetListener4); - }); - - return ; - } - - ReactDOM.render(, container); - Scheduler.unstable_flushAll(); - - let buttonElement = buttonRef.current; - dispatchClickEvent(buttonElement); - - expect(targetListener1).toHaveBeenCalledTimes(1); - expect(targetListener2).toHaveBeenCalledTimes(1); - expect(targetListener3).toHaveBeenCalledTimes(1); - expect(targetListener4).toHaveBeenCalledTimes(1); - - function Test2() { - const click1 = ReactDOM.unstable_useEvent('click'); - const click2 = ReactDOM.unstable_useEvent('click'); - const click3 = ReactDOM.unstable_useEvent('click'); - const click4 = ReactDOM.unstable_useEvent('click'); - - React.useEffect(() => { - click1.setListener(buttonRef.current, targetListener1); - click2.setListener(buttonRef.current, targetListener2); - click3.setListener(buttonRef.current, targetListener3); - click4.setListener(buttonRef.current, targetListener4); - }); - - return ; - } - - ReactDOM.render(, container); - Scheduler.unstable_flushAll(); - - buttonElement = buttonRef.current; - dispatchClickEvent(buttonElement); - expect(targetListener1).toHaveBeenCalledTimes(2); - expect(targetListener2).toHaveBeenCalledTimes(2); - expect(targetListener3).toHaveBeenCalledTimes(2); - expect(targetListener4).toHaveBeenCalledTimes(2); - }); - - // @gate experimental - it('should correctly handle stopPropagation corrrectly for target events', () => { - const buttonRef = React.createRef(); - const divRef = React.createRef(); - const clickEvent = jest.fn(); - - function Test() { - const click1 = ReactDOM.unstable_useEvent('click', { - bind: buttonRef, - }); - const click2 = ReactDOM.unstable_useEvent('click'); - - React.useEffect(() => { - click1.setListener(buttonRef.current, clickEvent); - click2.setListener(divRef.current, e => { - e.stopPropagation(); - }); - }); - - return ( - - ); - } - - ReactDOM.render(, container); - Scheduler.unstable_flushAll(); - - const divElement = divRef.current; - dispatchClickEvent(divElement); - expect(clickEvent).toHaveBeenCalledTimes(0); - }); - - // @gate experimental - it('should correctly handle stopPropagation corrrectly for many target events', () => { - const buttonRef = React.createRef(); - const targetListerner1 = jest.fn(e => e.stopPropagation()); - const targetListerner2 = jest.fn(e => e.stopPropagation()); - const targetListerner3 = jest.fn(e => e.stopPropagation()); - const targetListerner4 = jest.fn(e => e.stopPropagation()); - - function Test() { - const click1 = ReactDOM.unstable_useEvent('click'); - const click2 = ReactDOM.unstable_useEvent('click'); - const click3 = ReactDOM.unstable_useEvent('click'); - const click4 = ReactDOM.unstable_useEvent('click'); - - React.useEffect(() => { - click1.setListener(buttonRef.current, targetListerner1); - click2.setListener(buttonRef.current, targetListerner2); - click3.setListener(buttonRef.current, targetListerner3); - click4.setListener(buttonRef.current, targetListerner4); - }); - - return ; - } - - ReactDOM.render(, container); - Scheduler.unstable_flushAll(); - - const buttonElement = buttonRef.current; - dispatchClickEvent(buttonElement); - expect(targetListerner1).toHaveBeenCalledTimes(1); - expect(targetListerner2).toHaveBeenCalledTimes(1); - expect(targetListerner3).toHaveBeenCalledTimes(1); - expect(targetListerner4).toHaveBeenCalledTimes(1); - }); - - // @gate experimental - it('should correctly handle stopPropagation for mixed capture/bubbling target listeners', () => { - const buttonRef = React.createRef(); - const targetListerner1 = jest.fn(e => e.stopPropagation()); - const targetListerner2 = jest.fn(e => e.stopPropagation()); - const targetListerner3 = jest.fn(e => e.stopPropagation()); - const targetListerner4 = jest.fn(e => e.stopPropagation()); - - function Test() { - const click1 = ReactDOM.unstable_useEvent('click', { - capture: true, - }); - const click2 = ReactDOM.unstable_useEvent('click', { - capture: true, - }); - const click3 = ReactDOM.unstable_useEvent('click'); - const click4 = ReactDOM.unstable_useEvent('click'); - - React.useEffect(() => { - click1.setListener(buttonRef.current, targetListerner1); - click2.setListener(buttonRef.current, targetListerner2); - click3.setListener(buttonRef.current, targetListerner3); - click4.setListener(buttonRef.current, targetListerner4); - }); - - return ; - } - - ReactDOM.render(, container); - Scheduler.unstable_flushAll(); - - const buttonElement = buttonRef.current; - dispatchClickEvent(buttonElement); - expect(targetListerner1).toHaveBeenCalledTimes(1); - expect(targetListerner2).toHaveBeenCalledTimes(1); - expect(targetListerner3).toHaveBeenCalledTimes(1); - expect(targetListerner4).toHaveBeenCalledTimes(1); - }); - - // @gate experimental - it('should work with concurrent mode updates', async () => { - const log = []; - const ref = React.createRef(); - - function Test({counter}) { - const click = ReactDOM.unstable_useEvent('click'); - - React.useLayoutEffect(() => { - click.setListener(ref.current, () => { - log.push({counter}); - }); - }); - - Scheduler.unstable_yieldValue('Test'); - return ; - } - - const root = ReactDOM.createRoot(container); - root.render(); - - expect(Scheduler).toFlushAndYield(['Test']); - - // Click the button - dispatchClickEvent(ref.current); - expect(log).toEqual([{counter: 0}]); - - // Clear log - log.length = 0; - - // Increase counter - root.render(); - // Yield before committing - expect(Scheduler).toFlushAndYieldThrough(['Test']); - - // Click the button again - dispatchClickEvent(ref.current); - expect(log).toEqual([{counter: 0}]); - - // Clear log - log.length = 0; - - // Commit - expect(Scheduler).toFlushAndYield([]); - dispatchClickEvent(ref.current); - expect(log).toEqual([{counter: 1}]); - }); - - // @gate experimental - it('should correctly work for a basic "click" listener that upgrades', () => { - const clickEvent = jest.fn(); - const buttonRef = React.createRef(); - const button2Ref = React.createRef(); - - function Test2() { - const click = ReactDOM.unstable_useEvent('click', { - passive: false, - }); - - React.useEffect(() => { - click.setListener(button2Ref.current, clickEvent); - }); - - return ; - } - - function Test({extra}) { - const click = ReactDOM.unstable_useEvent('click', { - passive: true, - }); - - React.useEffect(() => { - click.setListener(buttonRef.current, clickEvent); - }); - - return ( - <> - - {extra && } - - ); - } - - ReactDOM.render(, container); - Scheduler.unstable_flushAll(); - - let button = buttonRef.current; - dispatchClickEvent(button); - expect(clickEvent).toHaveBeenCalledTimes(1); - - ReactDOM.render(, container); - Scheduler.unstable_flushAll(); - - clickEvent.mockClear(); - - button = button2Ref.current; - dispatchClickEvent(button); - expect(clickEvent).toHaveBeenCalledTimes(1); - }); - - // @gate experimental - it('should correctly work for a basic "click" listener that upgrades #2', () => { - const clickEvent = jest.fn(); - const buttonRef = React.createRef(); - const button2Ref = React.createRef(); - - function Test2() { - const click = ReactDOM.unstable_useEvent('click', { - passive: false, - }); - - React.useEffect(() => { - click.setListener(button2Ref.current, clickEvent); - }); - - return ; - } - - function Test({extra}) { - const click = ReactDOM.unstable_useEvent('click', { - passive: undefined, - }); - - React.useEffect(() => { - click.setListener(buttonRef.current, clickEvent); - }); - - return ( - <> - - {extra && } - - ); - } - - ReactDOM.render(, container); - Scheduler.unstable_flushAll(); - - let button = buttonRef.current; - dispatchClickEvent(button); - expect(clickEvent).toHaveBeenCalledTimes(1); - - ReactDOM.render(, container); - Scheduler.unstable_flushAll(); - - clickEvent.mockClear(); - - button = button2Ref.current; - dispatchClickEvent(button); - expect(clickEvent).toHaveBeenCalledTimes(1); - }); - - // @gate experimental - it('should correctly work for a basic "click" window listener', () => { - const log = []; - const clickEvent = jest.fn(event => { - log.push({ - eventPhase: event.eventPhase, - type: event.type, - currentTarget: event.currentTarget, - target: event.target, - }); - }); - - function Test() { - const click = ReactDOM.unstable_useEvent('click'); - - React.useEffect(() => { - click.setListener(window, clickEvent); - }); - - return ; - } - ReactDOM.render(, container); - Scheduler.unstable_flushAll(); - - expect(container.innerHTML).toBe( - '', - ); - - // Clicking outside the button should trigger the event callback - dispatchClickEvent(document.body); - expect(log[0]).toEqual({ - eventPhase: 3, - type: 'click', - currentTarget: window, - target: document.body, - }); - - // Unmounting the container and clicking should not work - ReactDOM.render(null, container); - Scheduler.unstable_flushAll(); - - dispatchClickEvent(document.body); - expect(clickEvent).toBeCalledTimes(1); - - // Re-rendering and clicking the body should work again - ReactDOM.render(, container); - Scheduler.unstable_flushAll(); - - dispatchClickEvent(document.body); - expect(clickEvent).toBeCalledTimes(2); - }); - - // @gate experimental - it('handle propagation of click events on the window', () => { - const buttonRef = React.createRef(); - const divRef = React.createRef(); - const log = []; - const onClick = jest.fn(e => log.push(['bubble', e.currentTarget])); - const onClickCapture = jest.fn(e => - log.push(['capture', e.currentTarget]), - ); - - function Test() { - const click = ReactDOM.unstable_useEvent('click'); - const clickCapture = ReactDOM.unstable_useEvent('click', { - capture: true, - }); - - React.useEffect(() => { - click.setListener(window, onClick); - clickCapture.setListener(window, onClickCapture); - click.setListener(buttonRef.current, onClick); - clickCapture.setListener(buttonRef.current, onClickCapture); - click.setListener(divRef.current, onClick); - clickCapture.setListener(divRef.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[0]).toEqual(['capture', window]); - expect(log[1]).toEqual(['capture', buttonElement]); - expect(log[2]).toEqual(['bubble', buttonElement]); - expect(log[3]).toEqual(['bubble', window]); - - log.length = 0; - onClick.mockClear(); - onClickCapture.mockClear(); - - const divElement = divRef.current; - dispatchClickEvent(divElement); - expect(onClick).toHaveBeenCalledTimes(3); - expect(onClickCapture).toHaveBeenCalledTimes(3); - expect(log[0]).toEqual(['capture', window]); - expect(log[1]).toEqual(['capture', buttonElement]); - expect(log[2]).toEqual(['capture', divElement]); - expect(log[3]).toEqual(['bubble', divElement]); - expect(log[4]).toEqual(['bubble', buttonElement]); - expect(log[5]).toEqual(['bubble', window]); - }); - - // @gate experimental - it('should correctly handle stopPropagation for mixed listeners', () => { - const buttonRef = React.createRef(); - const rootListerner1 = jest.fn(e => e.stopPropagation()); - const rootListerner2 = jest.fn(); - const targetListerner1 = jest.fn(); - const targetListerner2 = jest.fn(); - - function Test() { - const click1 = ReactDOM.unstable_useEvent('click', { - capture: true, - }); - const click2 = ReactDOM.unstable_useEvent('click', { - capture: true, - }); - const click3 = ReactDOM.unstable_useEvent('click'); - const click4 = ReactDOM.unstable_useEvent('click'); - - React.useEffect(() => { - click1.setListener(window, rootListerner1); - click2.setListener(buttonRef.current, targetListerner1); - click3.setListener(window, rootListerner2); - click4.setListener(buttonRef.current, targetListerner2); - }); - - return ; - } - - ReactDOM.render(, container); - Scheduler.unstable_flushAll(); - - const buttonElement = buttonRef.current; - dispatchClickEvent(buttonElement); - expect(rootListerner1).toHaveBeenCalledTimes(1); - expect(targetListerner1).toHaveBeenCalledTimes(0); - expect(targetListerner2).toHaveBeenCalledTimes(0); - expect(rootListerner2).toHaveBeenCalledTimes(0); - }); - - // @gate experimental - it('should correctly handle stopPropagation for delegated listeners', () => { - const buttonRef = React.createRef(); - const rootListerner1 = jest.fn(e => e.stopPropagation()); - const rootListerner2 = jest.fn(); - const rootListerner3 = jest.fn(e => e.stopPropagation()); - const rootListerner4 = jest.fn(); - - function Test() { - const click1 = ReactDOM.unstable_useEvent('click', { - capture: true, - }); - const click2 = ReactDOM.unstable_useEvent('click', { - capture: true, - }); - const click3 = ReactDOM.unstable_useEvent('click'); - const click4 = ReactDOM.unstable_useEvent('click'); - - React.useEffect(() => { - click1.setListener(window, rootListerner1); - click2.setListener(window, rootListerner2); - click3.setListener(window, rootListerner3); - click4.setListener(window, rootListerner4); - }); - - return ; - } - - ReactDOM.render(, container); - - Scheduler.unstable_flushAll(); - - const buttonElement = buttonRef.current; - dispatchClickEvent(buttonElement); - expect(rootListerner1).toHaveBeenCalledTimes(1); - expect(rootListerner2).toHaveBeenCalledTimes(1); - expect(rootListerner3).toHaveBeenCalledTimes(0); - expect(rootListerner4).toHaveBeenCalledTimes(0); - }); - - // @gate experimental - it('handle propagation of click events on the window and document', () => { - const buttonRef = React.createRef(); - const divRef = React.createRef(); - const log = []; - const onClick = jest.fn(e => log.push(['bubble', e.currentTarget])); - const onClickCapture = jest.fn(e => - log.push(['capture', e.currentTarget]), - ); - - function Test() { - const click = ReactDOM.unstable_useEvent('click'); - const clickCapture = ReactDOM.unstable_useEvent('click', { - capture: true, - }); - - React.useEffect(() => { - click.setListener(window, onClick); - clickCapture.setListener(window, onClickCapture); - click.setListener(document, onClick); - clickCapture.setListener(document, onClickCapture); - click.setListener(buttonRef.current, onClick); - clickCapture.setListener(buttonRef.current, onClickCapture); - click.setListener(divRef.current, onClick); - clickCapture.setListener(divRef.current, onClickCapture); - }); - - return ( - - ); - } - - ReactDOM.render(, container); - Scheduler.unstable_flushAll(); - - const buttonElement = buttonRef.current; - dispatchClickEvent(buttonElement); - expect(onClick).toHaveBeenCalledTimes(3); - expect(onClickCapture).toHaveBeenCalledTimes(3); - expect(log[0]).toEqual(['capture', window]); - expect(log[1]).toEqual(['capture', document]); - expect(log[2]).toEqual(['capture', buttonElement]); - expect(log[3]).toEqual(['bubble', buttonElement]); - expect(log[4]).toEqual(['bubble', document]); - expect(log[5]).toEqual(['bubble', window]); - - log.length = 0; - onClick.mockClear(); - onClickCapture.mockClear(); - - const divElement = divRef.current; - dispatchClickEvent(divElement); - expect(onClick).toHaveBeenCalledTimes(4); - expect(onClickCapture).toHaveBeenCalledTimes(4); - expect(log[0]).toEqual(['capture', window]); - expect(log[1]).toEqual(['capture', document]); - expect(log[2]).toEqual(['capture', buttonElement]); - expect(log[3]).toEqual(['capture', divElement]); - expect(log[4]).toEqual(['bubble', divElement]); - expect(log[5]).toEqual(['bubble', buttonElement]); - expect(log[6]).toEqual(['bubble', document]); - expect(log[7]).toEqual(['bubble', window]); - }); - - // @gate experimental - it('handles propagation of custom user events', () => { - const buttonRef = React.createRef(); - const divRef = React.createRef(); - const log = []; - const onCustomEvent = jest.fn(e => - log.push(['bubble', e.currentTarget]), - ); - const onCustomEventCapture = jest.fn(e => - log.push(['capture', e.currentTarget]), - ); - - function Test() { - let customEventHandle; - - // Test that we get a warning when we don't provide an explicit priortiy - expect(() => { - customEventHandle = ReactDOM.unstable_useEvent('custom-event'); - }).toWarnDev( - 'Warning: The event "type" provided to useEvent() does not have a known priority type. ' + - 'It is recommended to provide a "priority" option to specify a priority.', - ); - - customEventHandle = ReactDOM.unstable_useEvent('custom-event', { - priority: 0, // Discrete - }); - - const customCaptureHandle = ReactDOM.unstable_useEvent( - 'custom-event', - { - capture: true, - priority: 0, // Discrete - }, - ); - - React.useEffect(() => { - customEventHandle.setListener(buttonRef.current, onCustomEvent); - customCaptureHandle.setListener( - buttonRef.current, - onCustomEventCapture, - ); - customEventHandle.setListener(divRef.current, onCustomEvent); - customCaptureHandle.setListener( - divRef.current, - onCustomEventCapture, - ); - }); - - return ( - - ); - } - - ReactDOM.render(, container); - Scheduler.unstable_flushAll(); - - const buttonElement = buttonRef.current; - dispatchEvent(buttonElement, 'custom-event'); - expect(onCustomEvent).toHaveBeenCalledTimes(1); - expect(onCustomEventCapture).toHaveBeenCalledTimes(1); - expect(log[0]).toEqual(['capture', buttonElement]); - expect(log[1]).toEqual(['bubble', buttonElement]); - - const divElement = divRef.current; - dispatchEvent(divElement, 'custom-event'); - expect(onCustomEvent).toHaveBeenCalledTimes(3); - expect(onCustomEventCapture).toHaveBeenCalledTimes(3); - expect(log[2]).toEqual(['capture', buttonElement]); - expect(log[3]).toEqual(['capture', divElement]); - expect(log[4]).toEqual(['bubble', divElement]); - expect(log[5]).toEqual(['bubble', buttonElement]); - }); - - // @gate experimental - it('beforeblur and afterblur are called after a focused element is unmounted', () => { - const log = []; - // We have to persist here because we want to read relatedTarget later. - const onAfterBlur = jest.fn(e => { - e.persist(); - log.push(e.type); - }); - const onBeforeBlur = jest.fn(e => log.push(e.type)); - const innerRef = React.createRef(); - const innerRef2 = React.createRef(); - - const Component = ({show}) => { - const ref = React.useRef(null); - const afterBlurHandle = ReactDOM.unstable_useEvent('afterblur'); - const beforeBlurHandle = ReactDOM.unstable_useEvent('beforeblur'); - - React.useEffect(() => { - afterBlurHandle.setListener(document, onAfterBlur); - beforeBlurHandle.setListener(ref.current, onBeforeBlur); - }); - - return ( -
- {show && } -
-
- ); - }; - - ReactDOM.render(, container); - Scheduler.unstable_flushAll(); - - const inner = innerRef.current; - const target = createEventTarget(inner); - target.focus(); - expect(onBeforeBlur).toHaveBeenCalledTimes(0); - expect(onAfterBlur).toHaveBeenCalledTimes(0); - - ReactDOM.render(, container); - Scheduler.unstable_flushAll(); - - expect(onBeforeBlur).toHaveBeenCalledTimes(1); - expect(onAfterBlur).toHaveBeenCalledTimes(1); - expect(onAfterBlur).toHaveBeenCalledWith( - expect.objectContaining({relatedTarget: inner}), - ); - expect(log).toEqual(['beforeblur', 'afterblur']); - }); - - // @gate experimental - it('beforeblur and afterblur are called after a nested focused element is unmounted', () => { - const log = []; - // We have to persist here because we want to read relatedTarget later. - const onAfterBlur = jest.fn(e => { - e.persist(); - log.push(e.type); - }); - const onBeforeBlur = jest.fn(e => log.push(e.type)); - const innerRef = React.createRef(); - const innerRef2 = React.createRef(); - - const Component = ({show}) => { - const ref = React.useRef(null); - const afterBlurHandle = ReactDOM.unstable_useEvent('afterblur'); - const beforeBlurHandle = ReactDOM.unstable_useEvent('beforeblur'); - - React.useEffect(() => { - afterBlurHandle.setListener(document, onAfterBlur); - beforeBlurHandle.setListener(ref.current, onBeforeBlur); - }); - - return ( -
- {show && ( -
- -
- )} -
-
- ); - }; - - ReactDOM.render(, container); - Scheduler.unstable_flushAll(); - - const inner = innerRef.current; - const target = createEventTarget(inner); - target.focus(); - expect(onBeforeBlur).toHaveBeenCalledTimes(0); - expect(onAfterBlur).toHaveBeenCalledTimes(0); - - ReactDOM.render(, container); - Scheduler.unstable_flushAll(); - - expect(onBeforeBlur).toHaveBeenCalledTimes(1); - expect(onAfterBlur).toHaveBeenCalledTimes(1); - expect(onAfterBlur).toHaveBeenCalledWith( - expect.objectContaining({relatedTarget: inner}), - ); - expect(log).toEqual(['beforeblur', 'afterblur']); - }); - - // @gate experimental - it('beforeblur and afterblur are called after a focused element is suspended', () => { - const log = []; - // We have to persist here because we want to read relatedTarget later. - const onAfterBlur = jest.fn(e => { - e.persist(); - log.push(e.type); - }); - const onBeforeBlur = jest.fn(e => log.push(e.type)); - const innerRef = React.createRef(); - const Suspense = React.Suspense; - let suspend = false; - let resolve; - const promise = new Promise( - resolvePromise => (resolve = resolvePromise), - ); - - function Child() { - if (suspend) { - throw promise; - } else { - return ; - } - } - - const Component = () => { - const ref = React.useRef(null); - const afterBlurHandle = ReactDOM.unstable_useEvent('afterblur'); - const beforeBlurHandle = ReactDOM.unstable_useEvent('beforeblur'); - - React.useEffect(() => { - afterBlurHandle.setListener(document, onAfterBlur); - beforeBlurHandle.setListener(ref.current, onBeforeBlur); - }); - - return ( -
- - - -
- ); - }; - - const container2 = document.createElement('div'); - document.body.appendChild(container2); - - const root = ReactDOM.createRoot(container2); - - ReactTestUtils.act(() => { - root.render(); - }); - jest.runAllTimers(); - - const inner = innerRef.current; - const target = createEventTarget(inner); - target.focus(); - expect(onBeforeBlur).toHaveBeenCalledTimes(0); - expect(onAfterBlur).toHaveBeenCalledTimes(0); - - suspend = true; - ReactTestUtils.act(() => { - root.render(); - }); - jest.runAllTimers(); - - expect(onBeforeBlur).toHaveBeenCalledTimes(1); - expect(onAfterBlur).toHaveBeenCalledTimes(1); - expect(onAfterBlur).toHaveBeenCalledWith( - expect.objectContaining({relatedTarget: inner}), - ); - resolve(); - expect(log).toEqual(['beforeblur', 'afterblur']); - - 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'); - }); - - // @gate experimental - 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], - ]); - }); - - // @gate experimental - it('should not handle the target being a dangling text node within a scope', () => { - const clickEvent = jest.fn(); - const buttonRef = React.createRef(); - - const TestScope = React.unstable_createScope(); - - function Test() { - const click = ReactDOM.unstable_useEvent('click'); - const scopeRef = React.useRef(null); - - React.useEffect(() => { - click.setListener(scopeRef.current, clickEvent); - }); - - return ( - - ); - } - - ReactDOM.render(, container); - Scheduler.unstable_flushAll(); - - const textNode = buttonRef.current.firstChild; - dispatchClickEvent(textNode); - // This should not work, as the target instance will be the - // } - {show && } - {show && } - {show && } - {!show && } - {show && } - - -
- ); - }; - - ReactDOM.render(, container); - - inputRef.current.focus(); - expect(onBeforeBlurWithin).toHaveBeenCalledTimes(0); - expect(onAfterBlurWithin).toHaveBeenCalledTimes(0); - ReactDOM.render(, container); - expect(onBeforeBlurWithin).toHaveBeenCalledTimes(1); - expect(onAfterBlurWithin).toHaveBeenCalledTimes(1); - }); - - // @gate experimental - it('is called after a nested focused element is unmounted (with scope query)', () => { - const TestScope = React.unstable_createScope(); - const testScopeQuery = (type, props) => true; - let targetNodes; - let targetNode; - - const Component = ({show}) => { - const scopeRef = React.useRef(null); - useFocusWithin(scopeRef, { - onBeforeBlurWithin(event) { - const scope = scopeRef.current; - targetNode = innerRef.current; - targetNodes = scope.DO_NOT_USE_queryAllNodes(testScopeQuery); - }, - }); - - return ( - - {show && } - - ); - }; - - ReactDOM.render(, container); - - const inner = innerRef.current; - const target = createEventTarget(inner); - target.keydown({key: 'Tab'}); - target.focus(); - ReactDOM.render(, container); - expect(targetNodes).toEqual([targetNode]); - }); - - // @gate experimental - it('is called after a focused suspended element is hidden', () => { - const Suspense = React.Suspense; - let suspend = false; - let resolve; - const promise = new Promise(resolvePromise => (resolve = resolvePromise)); - - function Child() { - if (suspend) { - throw promise; - } else { - return ; - } - } - - const Component = ({show}) => { - useFocusWithin(ref, { - onBeforeBlurWithin, - onAfterBlurWithin, - }); - - return ( -
- - - -
- ); - }; - - const root = ReactDOM.createRoot(container2); - - act(() => { - root.render(); - }); - jest.runAllTimers(); - expect(container2.innerHTML).toBe('
'); - - const inner = innerRef.current; - const target = createEventTarget(inner); - target.keydown({key: 'Tab'}); - target.focus(); - expect(onBeforeBlurWithin).toHaveBeenCalledTimes(0); - expect(onAfterBlurWithin).toHaveBeenCalledTimes(0); - - suspend = true; - act(() => { - root.render(); - }); - jest.runAllTimers(); - expect(container2.innerHTML).toBe( - '
Loading...
', - ); - expect(onBeforeBlurWithin).toHaveBeenCalledTimes(1); - expect(onAfterBlurWithin).toHaveBeenCalledTimes(1); - resolve(); - }); - - // @gate experimental - it('is called after a focused suspended element is hidden then shown', () => { - const Suspense = React.Suspense; - let suspend = false; - let resolve; - const promise = new Promise(resolvePromise => (resolve = resolvePromise)); - const buttonRef = React.createRef(); - - function Child() { - if (suspend) { - throw promise; - } else { - return ; - } - } - - const Component = ({show}) => { - useFocusWithin(ref, { - onBeforeBlurWithin, - onAfterBlurWithin, - }); - - return ( -
- Loading...}> - - -
- ); - }; - - const root = ReactDOM.createRoot(container2); - - act(() => { - root.render(); - }); - jest.runAllTimers(); - - expect(onBeforeBlurWithin).toHaveBeenCalledTimes(0); - expect(onAfterBlurWithin).toHaveBeenCalledTimes(0); - - suspend = true; - act(() => { - root.render(); - }); - jest.runAllTimers(); - expect(onBeforeBlurWithin).toHaveBeenCalledTimes(0); - expect(onAfterBlurWithin).toHaveBeenCalledTimes(0); - - act(() => { - root.render(); - }); - jest.runAllTimers(); - expect(onBeforeBlurWithin).toHaveBeenCalledTimes(0); - expect(onAfterBlurWithin).toHaveBeenCalledTimes(0); - - buttonRef.current.focus(); - suspend = false; - act(() => { - root.render(); - }); - jest.runAllTimers(); - expect(onBeforeBlurWithin).toHaveBeenCalledTimes(1); - expect(onAfterBlurWithin).toHaveBeenCalledTimes(1); - - resolve(); - }); - }); -}); diff --git a/packages/react-native-renderer/src/ReactFabricHostConfig.js b/packages/react-native-renderer/src/ReactFabricHostConfig.js index 5b1d0edb8d..714c81d7dd 100644 --- a/packages/react-native-renderer/src/ReactFabricHostConfig.js +++ b/packages/react-native-renderer/src/ReactFabricHostConfig.js @@ -77,9 +77,6 @@ export type UpdatePayload = Object; export type TimeoutHandle = TimeoutID; export type NoTimeout = -1; -export type ReactListenerEvent = Object; -export type ReactListenerMap = Object; -export type ReactListener = Object; export type OpaqueIDType = void; export type RendererInspectionConfig = $ReadOnly<{| @@ -510,22 +507,6 @@ export function makeServerId(): OpaqueIDType { throw new Error('Not yet implemented'); } -export function registerEvent(event: any, rootContainerInstance: Container) { - throw new Error('Not yet implemented.'); -} - -export function mountEventListener(listener: any) { - throw new Error('Not yet implemented.'); -} - -export function unmountEventListener(listener: any) { - throw new Error('Not yet implemented.'); -} - -export function validateEventListenerTarget(target: any, listener: any) { - throw new Error('Not yet implemented.'); -} - export function beforeActiveInstanceBlur() { // noop } diff --git a/packages/react-native-renderer/src/ReactNativeHostConfig.js b/packages/react-native-renderer/src/ReactNativeHostConfig.js index 726c743247..8acf509953 100644 --- a/packages/react-native-renderer/src/ReactNativeHostConfig.js +++ b/packages/react-native-renderer/src/ReactNativeHostConfig.js @@ -28,10 +28,6 @@ import ReactNativeFiberHostComponent from './ReactNativeFiberHostComponent'; const {get: getViewConfigForType} = ReactNativeViewConfigRegistry; -export type ReactListenerEvent = Object; -export type ReactListenerMap = Object; -export type ReactListener = Object; - export type Type = string; export type Props = Object; export type Container = number; @@ -559,22 +555,6 @@ export function makeServerId(): OpaqueIDType { throw new Error('Not yet implemented'); } -export function registerEvent(event: any, rootContainerInstance: Container) { - throw new Error('Not yet implemented.'); -} - -export function mountEventListener(listener: any) { - throw new Error('Not yet implemented.'); -} - -export function unmountEventListener(listener: any) { - throw new Error('Not yet implemented.'); -} - -export function validateEventListenerTarget(target: any, listener: any) { - throw new Error('Not yet implemented.'); -} - export function beforeActiveInstanceBlur() { // noop } diff --git a/packages/react-reconciler/src/ReactFiberCommitWork.new.js b/packages/react-reconciler/src/ReactFiberCommitWork.new.js index fbd1b8b25c..0453c97cd3 100644 --- a/packages/react-reconciler/src/ReactFiberCommitWork.new.js +++ b/packages/react-reconciler/src/ReactFiberCommitWork.new.js @@ -36,7 +36,6 @@ import { enableSuspenseCallback, enableScopeAPI, runAllPassiveEffectDestroysBeforeCreates, - enableUseEventAPI, } from 'shared/ReactFeatureFlags'; import { FunctionComponent, @@ -1020,9 +1019,7 @@ function commitUnmount( if (enableDeprecatedFlareAPI) { unmountDeprecatedResponderListeners(current); } - if (enableDeprecatedFlareAPI || enableUseEventAPI) { - beforeRemoveInstance(current.stateNode); - } + beforeRemoveInstance(current.stateNode); safelyDetachRef(current); return; } diff --git a/packages/react-reconciler/src/ReactFiberCommitWork.old.js b/packages/react-reconciler/src/ReactFiberCommitWork.old.js index 1d154c7b75..50153cfd7a 100644 --- a/packages/react-reconciler/src/ReactFiberCommitWork.old.js +++ b/packages/react-reconciler/src/ReactFiberCommitWork.old.js @@ -36,7 +36,6 @@ import { enableSuspenseCallback, enableScopeAPI, runAllPassiveEffectDestroysBeforeCreates, - enableUseEventAPI, } from 'shared/ReactFeatureFlags'; import { FunctionComponent, @@ -1020,9 +1019,7 @@ function commitUnmount( if (enableDeprecatedFlareAPI) { unmountDeprecatedResponderListeners(current); } - if (enableDeprecatedFlareAPI || enableUseEventAPI) { - beforeRemoveInstance(current.stateNode); - } + beforeRemoveInstance(current.stateNode); safelyDetachRef(current); return; } diff --git a/packages/react-reconciler/src/ReactFiberHooks.new.js b/packages/react-reconciler/src/ReactFiberHooks.new.js index 3d92e92238..ef3abee220 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.new.js +++ b/packages/react-reconciler/src/ReactFiberHooks.new.js @@ -14,7 +14,6 @@ import type { ReactEventResponder, ReactContext, ReactEventResponderListener, - ReactScopeMethods, } from 'shared/ReactTypes'; import type {Fiber, Dispatcher} from './ReactInternalTypes'; import type {ExpirationTime} from './ReactFiberExpirationTime.new'; @@ -22,15 +21,10 @@ import type {HookEffectTag} from './ReactHookEffectTags'; import type {SuspenseConfig} from './ReactFiberSuspenseConfig'; import type {ReactPriorityLevel} from './ReactInternalTypes'; import type {FiberRoot} from './ReactInternalTypes'; -import type { - OpaqueIDType, - ReactListenerEvent, - ReactListenerMap, - ReactListener, -} from './ReactFiberHostConfig'; +import type {OpaqueIDType} from './ReactFiberHostConfig'; import ReactSharedInternals from 'shared/ReactSharedInternals'; -import {enableDebugTracing, enableUseEventAPI} from 'shared/ReactFeatureFlags'; +import {enableDebugTracing} from 'shared/ReactFeatureFlags'; import {markRootExpiredAtTime} from './ReactFiberRoot.new'; import { @@ -46,7 +40,6 @@ import { Passive as PassiveEffect, } from './ReactSideEffectTags'; import { - NoEffect as NoHookEffect, HasEffect as HookHasEffect, Layout as HookLayout, Passive as HookPassive, @@ -63,12 +56,6 @@ import { markUnprocessedUpdateTime, priorityLevelToLabel, } from './ReactFiberWorkLoop.new'; -import { - registerEvent, - mountEventListener as mountHostEventListener, - unmountEventListener as unmountHostEventListener, - validateEventListenerTarget, -} from './ReactFiberHostConfig'; import invariant from 'shared/invariant'; import getComponentName from 'shared/getComponentName'; @@ -95,7 +82,6 @@ import { setWorkInProgressVersion, warnAboutMultipleRenderersDEV, } from './ReactMutableSource.new'; -import {getRootHostContainer} from './ReactFiberHostContext.new'; import {getIsRendering} from './ReactCurrentFiber'; import {logStateUpdateScheduled} from './DebugTracing'; @@ -136,7 +122,6 @@ export type HookType = | 'useDeferredValue' | 'useTransition' | 'useMutableSource' - | 'useEvent' | 'useOpaqueIdentifier'; let didWarnAboutMismatchedHooksForComponent; @@ -1760,158 +1745,6 @@ function dispatchAction( } } -const noOpMount = () => {}; - -function validateNotInFunctionRender(): boolean { - if (currentlyRenderingFiber === null) { - return true; - } - if (__DEV__) { - console.warn( - 'Event listener methods from useEvent() cannot be called during render.' + - ' These methods should be called in an effect or event callback outside the render.', - ); - } - return false; -} - -function createReactListener( - event: ReactListenerEvent, - callback: (SyntheticEvent) => void, - target: EventTarget | ReactScopeMethods, - destroy: Node => void, -): ReactListener { - return { - callback, - destroy, - event, - target, - }; -} - -function mountEventListener(event: ReactListenerEvent): ReactListenerMap { - if (enableUseEventAPI) { - const hook = mountWorkInProgressHook(); - const listenerMap: Map< - EventTarget | ReactScopeMethods, - ReactListener, - > = new Map(); - const rootContainerInstance = getRootHostContainer(); - - // Register the event to the current root to ensure event - // replaying can pick up the event ahead of time. - registerEvent(event, rootContainerInstance); - - const clear = () => { - if (validateNotInFunctionRender()) { - const listeners = Array.from(listenerMap.values()); - for (let i = 0; i < listeners.length; i++) { - unmountHostEventListener(listeners[i]); - } - listenerMap.clear(); - } - }; - - const destroy = (target: Node) => { - // We don't need to call detachListenerFromInstance - // here as this method should only ever be called - // from renderers that need to remove the instance - // from the map representing an instance that still - // holds a reference to the listenerMap. This means - // things like "window" listeners on ReactDOM should - // never enter this call path as the the instance in - // those cases would be that of "window", which - // should be handled via an optimized route in the - // renderer, making less overhead here. If we change - // this heuristic we should update this path to make - // sure we call detachListenerFromInstance. - listenerMap.delete(target); - }; - - const reactListenerMap: ReactListenerMap = { - clear, - setListener( - target: EventTarget | ReactScopeMethods, - callback: ?(SyntheticEvent) => void, - ): void { - if ( - validateNotInFunctionRender() && - validateEventListenerTarget(target, callback) - ) { - let listener = listenerMap.get(target); - if (listener === undefined) { - if (callback == null) { - return; - } - listener = createReactListener(event, callback, target, destroy); - listenerMap.set(target, listener); - } else { - if (callback == null) { - listenerMap.delete(target); - unmountHostEventListener(listener); - return; - } - listener.callback = callback; - } - mountHostEventListener(listener); - } - }, - }; - // In order to clear up upon the hook unmounting, - // we ensure we set the effecrt tag so that we visit - // this effect in the commit phase, so we can handle - // clean-up accordingly. - currentlyRenderingFiber.effectTag |= UpdateEffect; - pushEffect(NoHookEffect, noOpMount, clear, null); - hook.memoizedState = [reactListenerMap, event, clear]; - return reactListenerMap; - } - // To make Flow not complain - return (undefined: any); -} - -function updateEventListener(event: ReactListenerEvent): ReactListenerMap { - if (enableUseEventAPI) { - const hook = updateWorkInProgressHook(); - const [reactListenerMap, memoizedEvent, clear] = hook.memoizedState; - if (__DEV__) { - if (memoizedEvent.type !== event.type) { - console.warn( - 'The event type argument passed to the useEvent() hook was different between renders.' + - ' The event type is static and should never change between renders.', - ); - } - if (memoizedEvent.capture !== event.capture) { - console.warn( - 'The "capture" option passed to the useEvent() hook was different between renders.' + - ' The "capture" option is static and should never change between renders.', - ); - } - if (memoizedEvent.priority !== event.priority) { - console.warn( - 'The "priority" option passed to the useEvent() hook was different between renders.' + - ' The "priority" option is static and should never change between renders.', - ); - } - if (memoizedEvent.passive !== event.passive) { - console.warn( - 'The "passive" option passed to the useEvent() hook was different between renders.' + - ' The "passive" option is static and should never change between renders.', - ); - } - } - // In order to clear up upon the hook unmounting, - // we ensure we set the effecrt tag so that we visit - // this effect in the commit phase, so we can handle - // clean-up accordingly. - currentlyRenderingFiber.effectTag |= UpdateEffect; - pushEffect(NoHookEffect, noOpMount, clear, null); - return reactListenerMap; - } - // To make Flow not complain - return (undefined: any); -} - export const ContextOnlyDispatcher: Dispatcher = { readContext, @@ -1929,7 +1762,6 @@ export const ContextOnlyDispatcher: Dispatcher = { useDeferredValue: throwInvalidHookError, useTransition: throwInvalidHookError, useMutableSource: throwInvalidHookError, - useEvent: throwInvalidHookError, useOpaqueIdentifier: throwInvalidHookError, }; @@ -1950,7 +1782,6 @@ const HooksDispatcherOnMount: Dispatcher = { useDeferredValue: mountDeferredValue, useTransition: mountTransition, useMutableSource: mountMutableSource, - useEvent: mountEventListener, useOpaqueIdentifier: mountOpaqueIdentifier, }; @@ -1971,7 +1802,6 @@ const HooksDispatcherOnUpdate: Dispatcher = { useDeferredValue: updateDeferredValue, useTransition: updateTransition, useMutableSource: updateMutableSource, - useEvent: updateEventListener, useOpaqueIdentifier: updateOpaqueIdentifier, }; @@ -1992,7 +1822,6 @@ const HooksDispatcherOnRerender: Dispatcher = { useDeferredValue: rerenderDeferredValue, useTransition: rerenderTransition, useMutableSource: updateMutableSource, - useEvent: updateEventListener, useOpaqueIdentifier: rerenderOpaqueIdentifier, }; @@ -2151,11 +1980,6 @@ if (__DEV__) { mountHookTypesDev(); return mountMutableSource(source, getSnapshot, subscribe); }, - useEvent(event: ReactListenerEvent): ReactListenerMap { - currentHookNameInDev = 'useEvent'; - mountHookTypesDev(); - return mountEventListener(event); - }, useOpaqueIdentifier(): OpaqueIDType | void { currentHookNameInDev = 'useOpaqueIdentifier'; mountHookTypesDev(); @@ -2286,11 +2110,6 @@ if (__DEV__) { updateHookTypesDev(); return mountMutableSource(source, getSnapshot, subscribe); }, - useEvent(event: ReactListenerEvent): ReactListenerMap { - currentHookNameInDev = 'useEvent'; - updateHookTypesDev(); - return mountEventListener(event); - }, useOpaqueIdentifier(): OpaqueIDType | void { currentHookNameInDev = 'useOpaqueIdentifier'; updateHookTypesDev(); @@ -2421,11 +2240,6 @@ if (__DEV__) { updateHookTypesDev(); return updateMutableSource(source, getSnapshot, subscribe); }, - useEvent(event: ReactListenerEvent): ReactListenerMap { - currentHookNameInDev = 'useEvent'; - updateHookTypesDev(); - return updateEventListener(event); - }, useOpaqueIdentifier(): OpaqueIDType | void { currentHookNameInDev = 'useOpaqueIdentifier'; updateHookTypesDev(); @@ -2557,11 +2371,6 @@ if (__DEV__) { updateHookTypesDev(); return updateMutableSource(source, getSnapshot, subscribe); }, - useEvent(event: ReactListenerEvent): ReactListenerMap { - currentHookNameInDev = 'useEvent'; - updateHookTypesDev(); - return updateEventListener(event); - }, useOpaqueIdentifier(): OpaqueIDType | void { currentHookNameInDev = 'useOpaqueIdentifier'; updateHookTypesDev(); @@ -2707,12 +2516,6 @@ if (__DEV__) { mountHookTypesDev(); return mountMutableSource(source, getSnapshot, subscribe); }, - useEvent(event: ReactListenerEvent): ReactListenerMap { - currentHookNameInDev = 'useEvent'; - warnInvalidHookAccess(); - mountHookTypesDev(); - return mountEventListener(event); - }, useOpaqueIdentifier(): OpaqueIDType | void { currentHookNameInDev = 'useOpaqueIdentifier'; warnInvalidHookAccess(); @@ -2859,12 +2662,6 @@ if (__DEV__) { updateHookTypesDev(); return updateMutableSource(source, getSnapshot, subscribe); }, - useEvent(event: ReactListenerEvent): ReactListenerMap { - currentHookNameInDev = 'useEvent'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return updateEventListener(event); - }, useOpaqueIdentifier(): OpaqueIDType | void { currentHookNameInDev = 'useOpaqueIdentifier'; warnInvalidHookAccess(); @@ -3012,12 +2809,6 @@ if (__DEV__) { updateHookTypesDev(); return updateMutableSource(source, getSnapshot, subscribe); }, - useEvent(event: ReactListenerEvent): ReactListenerMap { - currentHookNameInDev = 'useEvent'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return updateEventListener(event); - }, useOpaqueIdentifier(): OpaqueIDType | void { currentHookNameInDev = 'useOpaqueIdentifier'; warnInvalidHookAccess(); diff --git a/packages/react-reconciler/src/ReactFiberHooks.old.js b/packages/react-reconciler/src/ReactFiberHooks.old.js index 37fcc9012c..d3b83165ea 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.old.js +++ b/packages/react-reconciler/src/ReactFiberHooks.old.js @@ -14,7 +14,6 @@ import type { ReactEventResponder, ReactContext, ReactEventResponderListener, - ReactScopeMethods, } from 'shared/ReactTypes'; import type {Fiber, Dispatcher} from './ReactInternalTypes'; import type {ExpirationTime} from './ReactFiberExpirationTime.old'; @@ -22,15 +21,10 @@ import type {HookEffectTag} from './ReactHookEffectTags'; import type {SuspenseConfig} from './ReactFiberSuspenseConfig'; import type {ReactPriorityLevel} from './ReactInternalTypes'; import type {FiberRoot} from './ReactInternalTypes'; -import type { - OpaqueIDType, - ReactListenerEvent, - ReactListenerMap, - ReactListener, -} from './ReactFiberHostConfig'; +import type {OpaqueIDType} from './ReactFiberHostConfig'; import ReactSharedInternals from 'shared/ReactSharedInternals'; -import {enableDebugTracing, enableUseEventAPI} from 'shared/ReactFeatureFlags'; +import {enableDebugTracing} from 'shared/ReactFeatureFlags'; import {markRootExpiredAtTime} from './ReactFiberRoot.old'; import {NoMode, BlockingMode, DebugTracingMode} from './ReactTypeOfMode'; @@ -46,7 +40,6 @@ import { Passive as PassiveEffect, } from './ReactSideEffectTags'; import { - NoEffect as NoHookEffect, HasEffect as HookHasEffect, Layout as HookLayout, Passive as HookPassive, @@ -63,12 +56,6 @@ import { markUnprocessedUpdateTime, priorityLevelToLabel, } from './ReactFiberWorkLoop.old'; -import { - registerEvent, - mountEventListener as mountHostEventListener, - unmountEventListener as unmountHostEventListener, - validateEventListenerTarget, -} from './ReactFiberHostConfig'; import invariant from 'shared/invariant'; import getComponentName from 'shared/getComponentName'; @@ -95,7 +82,6 @@ import { setWorkInProgressVersion, warnAboutMultipleRenderersDEV, } from './ReactMutableSource.old'; -import {getRootHostContainer} from './ReactFiberHostContext.old'; import {getIsRendering} from './ReactCurrentFiber'; import {logStateUpdateScheduled} from './DebugTracing'; @@ -133,7 +119,6 @@ export type HookType = | 'useDeferredValue' | 'useTransition' | 'useMutableSource' - | 'useEvent' | 'useOpaqueIdentifier'; let didWarnAboutMismatchedHooksForComponent; @@ -1755,158 +1740,6 @@ function dispatchAction( } } -const noOpMount = () => {}; - -function validateNotInFunctionRender(): boolean { - if (currentlyRenderingFiber === null) { - return true; - } - if (__DEV__) { - console.warn( - 'Event listener methods from useEvent() cannot be called during render.' + - ' These methods should be called in an effect or event callback outside the render.', - ); - } - return false; -} - -function createReactListener( - event: ReactListenerEvent, - callback: (SyntheticEvent) => void, - target: EventTarget | ReactScopeMethods, - destroy: Node => void, -): ReactListener { - return { - callback, - destroy, - event, - target, - }; -} - -function mountEventListener(event: ReactListenerEvent): ReactListenerMap { - if (enableUseEventAPI) { - const hook = mountWorkInProgressHook(); - const listenerMap: Map< - EventTarget | ReactScopeMethods, - ReactListener, - > = new Map(); - const rootContainerInstance = getRootHostContainer(); - - // Register the event to the current root to ensure event - // replaying can pick up the event ahead of time. - registerEvent(event, rootContainerInstance); - - const clear = () => { - if (validateNotInFunctionRender()) { - const listeners = Array.from(listenerMap.values()); - for (let i = 0; i < listeners.length; i++) { - unmountHostEventListener(listeners[i]); - } - listenerMap.clear(); - } - }; - - const destroy = (target: Node) => { - // We don't need to call detachListenerFromInstance - // here as this method should only ever be called - // from renderers that need to remove the instance - // from the map representing an instance that still - // holds a reference to the listenerMap. This means - // things like "window" listeners on ReactDOM should - // never enter this call path as the the instance in - // those cases would be that of "window", which - // should be handled via an optimized route in the - // renderer, making less overhead here. If we change - // this heuristic we should update this path to make - // sure we call detachListenerFromInstance. - listenerMap.delete(target); - }; - - const reactListenerMap: ReactListenerMap = { - clear, - setListener( - target: EventTarget | ReactScopeMethods, - callback: ?(SyntheticEvent) => void, - ): void { - if ( - validateNotInFunctionRender() && - validateEventListenerTarget(target, callback) - ) { - let listener = listenerMap.get(target); - if (listener === undefined) { - if (callback == null) { - return; - } - listener = createReactListener(event, callback, target, destroy); - listenerMap.set(target, listener); - } else { - if (callback == null) { - listenerMap.delete(target); - unmountHostEventListener(listener); - return; - } - listener.callback = callback; - } - mountHostEventListener(listener); - } - }, - }; - // In order to clear up upon the hook unmounting, - // we ensure we set the effecrt tag so that we visit - // this effect in the commit phase, so we can handle - // clean-up accordingly. - currentlyRenderingFiber.effectTag |= UpdateEffect; - pushEffect(NoHookEffect, noOpMount, clear, null); - hook.memoizedState = [reactListenerMap, event, clear]; - return reactListenerMap; - } - // To make Flow not complain - return (undefined: any); -} - -function updateEventListener(event: ReactListenerEvent): ReactListenerMap { - if (enableUseEventAPI) { - const hook = updateWorkInProgressHook(); - const [reactListenerMap, memoizedEvent, clear] = hook.memoizedState; - if (__DEV__) { - if (memoizedEvent.type !== event.type) { - console.warn( - 'The event type argument passed to the useEvent() hook was different between renders.' + - ' The event type is static and should never change between renders.', - ); - } - if (memoizedEvent.capture !== event.capture) { - console.warn( - 'The "capture" option passed to the useEvent() hook was different between renders.' + - ' The "capture" option is static and should never change between renders.', - ); - } - if (memoizedEvent.priority !== event.priority) { - console.warn( - 'The "priority" option passed to the useEvent() hook was different between renders.' + - ' The "priority" option is static and should never change between renders.', - ); - } - if (memoizedEvent.passive !== event.passive) { - console.warn( - 'The "passive" option passed to the useEvent() hook was different between renders.' + - ' The "passive" option is static and should never change between renders.', - ); - } - } - // In order to clear up upon the hook unmounting, - // we ensure we set the effecrt tag so that we visit - // this effect in the commit phase, so we can handle - // clean-up accordingly. - currentlyRenderingFiber.effectTag |= UpdateEffect; - pushEffect(NoHookEffect, noOpMount, clear, null); - return reactListenerMap; - } - // To make Flow not complain - return (undefined: any); -} - export const ContextOnlyDispatcher: Dispatcher = { readContext, @@ -1924,7 +1757,6 @@ export const ContextOnlyDispatcher: Dispatcher = { useDeferredValue: throwInvalidHookError, useTransition: throwInvalidHookError, useMutableSource: throwInvalidHookError, - useEvent: throwInvalidHookError, useOpaqueIdentifier: throwInvalidHookError, }; @@ -1945,7 +1777,6 @@ const HooksDispatcherOnMount: Dispatcher = { useDeferredValue: mountDeferredValue, useTransition: mountTransition, useMutableSource: mountMutableSource, - useEvent: mountEventListener, useOpaqueIdentifier: mountOpaqueIdentifier, }; @@ -1966,7 +1797,6 @@ const HooksDispatcherOnUpdate: Dispatcher = { useDeferredValue: updateDeferredValue, useTransition: updateTransition, useMutableSource: updateMutableSource, - useEvent: updateEventListener, useOpaqueIdentifier: updateOpaqueIdentifier, }; @@ -1987,7 +1817,6 @@ const HooksDispatcherOnRerender: Dispatcher = { useDeferredValue: rerenderDeferredValue, useTransition: rerenderTransition, useMutableSource: updateMutableSource, - useEvent: updateEventListener, useOpaqueIdentifier: rerenderOpaqueIdentifier, }; @@ -2146,11 +1975,6 @@ if (__DEV__) { mountHookTypesDev(); return mountMutableSource(source, getSnapshot, subscribe); }, - useEvent(event: ReactListenerEvent): ReactListenerMap { - currentHookNameInDev = 'useEvent'; - mountHookTypesDev(); - return mountEventListener(event); - }, useOpaqueIdentifier(): OpaqueIDType | void { currentHookNameInDev = 'useOpaqueIdentifier'; mountHookTypesDev(); @@ -2281,11 +2105,6 @@ if (__DEV__) { updateHookTypesDev(); return mountMutableSource(source, getSnapshot, subscribe); }, - useEvent(event: ReactListenerEvent): ReactListenerMap { - currentHookNameInDev = 'useEvent'; - updateHookTypesDev(); - return mountEventListener(event); - }, useOpaqueIdentifier(): OpaqueIDType | void { currentHookNameInDev = 'useOpaqueIdentifier'; updateHookTypesDev(); @@ -2416,11 +2235,6 @@ if (__DEV__) { updateHookTypesDev(); return updateMutableSource(source, getSnapshot, subscribe); }, - useEvent(event: ReactListenerEvent): ReactListenerMap { - currentHookNameInDev = 'useEvent'; - updateHookTypesDev(); - return updateEventListener(event); - }, useOpaqueIdentifier(): OpaqueIDType | void { currentHookNameInDev = 'useOpaqueIdentifier'; updateHookTypesDev(); @@ -2552,11 +2366,6 @@ if (__DEV__) { updateHookTypesDev(); return updateMutableSource(source, getSnapshot, subscribe); }, - useEvent(event: ReactListenerEvent): ReactListenerMap { - currentHookNameInDev = 'useEvent'; - updateHookTypesDev(); - return updateEventListener(event); - }, useOpaqueIdentifier(): OpaqueIDType | void { currentHookNameInDev = 'useOpaqueIdentifier'; updateHookTypesDev(); @@ -2702,12 +2511,6 @@ if (__DEV__) { mountHookTypesDev(); return mountMutableSource(source, getSnapshot, subscribe); }, - useEvent(event: ReactListenerEvent): ReactListenerMap { - currentHookNameInDev = 'useEvent'; - warnInvalidHookAccess(); - mountHookTypesDev(); - return mountEventListener(event); - }, useOpaqueIdentifier(): OpaqueIDType | void { currentHookNameInDev = 'useOpaqueIdentifier'; warnInvalidHookAccess(); @@ -2854,12 +2657,6 @@ if (__DEV__) { updateHookTypesDev(); return updateMutableSource(source, getSnapshot, subscribe); }, - useEvent(event: ReactListenerEvent): ReactListenerMap { - currentHookNameInDev = 'useEvent'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return updateEventListener(event); - }, useOpaqueIdentifier(): OpaqueIDType | void { currentHookNameInDev = 'useOpaqueIdentifier'; warnInvalidHookAccess(); @@ -3007,12 +2804,6 @@ if (__DEV__) { updateHookTypesDev(); return updateMutableSource(source, getSnapshot, subscribe); }, - useEvent(event: ReactListenerEvent): ReactListenerMap { - currentHookNameInDev = 'useEvent'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return updateEventListener(event); - }, useOpaqueIdentifier(): OpaqueIDType | void { currentHookNameInDev = 'useOpaqueIdentifier'; warnInvalidHookAccess(); diff --git a/packages/react-reconciler/src/ReactInternalTypes.js b/packages/react-reconciler/src/ReactInternalTypes.js index 1c37422388..b394f5f449 100644 --- a/packages/react-reconciler/src/ReactInternalTypes.js +++ b/packages/react-reconciler/src/ReactInternalTypes.js @@ -18,10 +18,7 @@ import type { MutableSourceGetSnapshotFn, MutableSource, } from 'shared/ReactTypes'; -import type { - SuspenseInstance, - ReactListenerEvent, -} from './ReactFiberHostConfig'; +import type {SuspenseInstance} from './ReactFiberHostConfig'; import type {WorkTag} from './ReactWorkTags'; import type {TypeOfMode} from './ReactTypeOfMode'; import type {SideEffectTag} from './ReactSideEffectTags'; @@ -32,10 +29,7 @@ import type {RootTag} from './ReactRootTags'; import type {TimeoutHandle, NoTimeout} from './ReactFiberHostConfig'; import type {Wakeable} from 'shared/ReactTypes'; import type {Interaction} from 'scheduler/src/Tracing'; -import type { - OpaqueIDType, - ReactListenerMap, -} from 'react-reconciler/src/ReactFiberHostConfig'; +import type {OpaqueIDType} from 'react-reconciler/src/ReactFiberHostConfig'; import type {SuspenseConfig, TimeoutConfig} from './ReactFiberSuspenseConfig'; export type ReactPriorityLevel = 99 | 98 | 97 | 96 | 95 | 90; @@ -325,6 +319,5 @@ export type Dispatcher = {| getSnapshot: MutableSourceGetSnapshotFn, subscribe: MutableSourceSubscribeFn, ): Snapshot, - useEvent(event: ReactListenerEvent): ReactListenerMap, useOpaqueIdentifier(): OpaqueIDType | void, |}; diff --git a/packages/react-reconciler/src/forks/ReactFiberHostConfig.custom.js b/packages/react-reconciler/src/forks/ReactFiberHostConfig.custom.js index eb3447f3f9..3d2544b5b2 100644 --- a/packages/react-reconciler/src/forks/ReactFiberHostConfig.custom.js +++ b/packages/react-reconciler/src/forks/ReactFiberHostConfig.custom.js @@ -40,9 +40,6 @@ export opaque type NoTimeout = mixed; // eslint-disable-line no-undef export opaque type RendererInspectionConfig = mixed; // eslint-disable-line no-undef export opaque type OpaqueIDType = mixed; export type EventResponder = any; -export type ReactListenerEvent = Object; -export type ReactListenerMap = Object; -export type ReactListener = Object; export const getPublicInstance = $$$hostConfig.getPublicInstance; export const getRootHostContext = $$$hostConfig.getRootHostContext; @@ -78,11 +75,6 @@ export const shouldUpdateFundamentalComponent = $$$hostConfig.shouldUpdateFundamentalComponent; export const getInstanceFromNode = $$$hostConfig.getInstanceFromNode; export const beforeRemoveInstance = $$$hostConfig.beforeRemoveInstance; -export const registerEvent = $$$hostConfig.registerEvent; -export const mountEventListener = $$$hostConfig.mountEventListener; -export const unmountEventListener = $$$hostConfig.unmountEventListener; -export const validateEventListenerTarget = - $$$hostConfig.validateEventListenerTarget; export const isOpaqueHydratingObject = $$$hostConfig.isOpaqueHydratingObject; export const makeOpaqueHydratingObject = $$$hostConfig.makeOpaqueHydratingObject; diff --git a/packages/react-test-renderer/src/ReactTestHostConfig.js b/packages/react-test-renderer/src/ReactTestHostConfig.js index b9c2c4adf4..bcf31c04b0 100644 --- a/packages/react-test-renderer/src/ReactTestHostConfig.js +++ b/packages/react-test-renderer/src/ReactTestHostConfig.js @@ -52,9 +52,6 @@ export opaque type OpaqueIDType = valueOf: () => string | void, }; -export type ReactListenerEvent = Object; -export type ReactListenerMap = Object; -export type ReactListener = Object; export type RendererInspectionConfig = $ReadOnly<{||}>; export * from 'react-reconciler/src/ReactFiberHostConfigWithNoPersistence'; @@ -431,22 +428,6 @@ export function makeOpaqueHydratingObject( }; } -export function registerEvent(event: any, rootContainerInstance: Container) { - throw new Error('Not yet implemented.'); -} - -export function mountEventListener(listener: any) { - throw new Error('Not yet implemented.'); -} - -export function unmountEventListener(listener: any) { - throw new Error('Not yet implemented.'); -} - -export function validateEventListenerTarget(target: any, listener: any) { - throw new Error('Not yet implemented.'); -} - export function beforeActiveInstanceBlur() { // noop } diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js index a683f4caba..8cd92e8ec8 100644 --- a/packages/shared/ReactFeatureFlags.js +++ b/packages/shared/ReactFeatureFlags.js @@ -57,9 +57,6 @@ export const enableFundamentalAPI = false; // Experimental Scope support. export const enableScopeAPI = false; -// Experimental useEvent support. -export const enableUseEventAPI = false; - // New API for JSX transforms to target - https://github.com/reactjs/rfcs/pull/107 // We will enforce mocking scheduler with scheduler/unstable_mock at some point. (v17?) diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb.js b/packages/shared/forks/ReactFeatureFlags.native-fb.js index 0a70e8d2d7..4c64cde394 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.native-fb.js @@ -27,7 +27,6 @@ export const warnAboutDeprecatedLifecycles = true; export const enableDeprecatedFlareAPI = false; export const enableFundamentalAPI = false; export const enableScopeAPI = false; -export const enableUseEventAPI = false; export const warnAboutUnmockedScheduler = true; export const enableSuspenseCallback = false; export const warnAboutDefaultPropsOnFunctionComponents = false; diff --git a/packages/shared/forks/ReactFeatureFlags.native-oss.js b/packages/shared/forks/ReactFeatureFlags.native-oss.js index 7644b11c36..c9c2e3a886 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-oss.js +++ b/packages/shared/forks/ReactFeatureFlags.native-oss.js @@ -26,7 +26,6 @@ export const enableSchedulerDebugging = false; export const enableDeprecatedFlareAPI = false; export const enableFundamentalAPI = false; export const enableScopeAPI = false; -export const enableUseEventAPI = false; export const warnAboutUnmockedScheduler = false; export const enableSuspenseCallback = false; export const warnAboutDefaultPropsOnFunctionComponents = false; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.js index 0e6b4efd9f..d942a87bb8 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.js @@ -26,7 +26,6 @@ export const enableSchedulerDebugging = false; export const enableDeprecatedFlareAPI = false; export const enableFundamentalAPI = false; export const enableScopeAPI = false; -export const enableUseEventAPI = false; export const warnAboutUnmockedScheduler = false; export const enableSuspenseCallback = false; export const warnAboutDefaultPropsOnFunctionComponents = false; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js index 3a5d6649f0..abfbe484ba 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js @@ -26,7 +26,6 @@ export const disableInputAttributeSyncing = false; export const enableDeprecatedFlareAPI = true; export const enableFundamentalAPI = false; export const enableScopeAPI = true; -export const enableUseEventAPI = true; export const warnAboutUnmockedScheduler = true; export const enableSuspenseCallback = true; export const warnAboutDefaultPropsOnFunctionComponents = false; diff --git a/packages/shared/forks/ReactFeatureFlags.testing.js b/packages/shared/forks/ReactFeatureFlags.testing.js index 53000c93ed..69a5714d66 100644 --- a/packages/shared/forks/ReactFeatureFlags.testing.js +++ b/packages/shared/forks/ReactFeatureFlags.testing.js @@ -26,7 +26,6 @@ export const enableSchedulerDebugging = false; export const enableDeprecatedFlareAPI = false; export const enableFundamentalAPI = false; export const enableScopeAPI = false; -export const enableUseEventAPI = false; export const warnAboutUnmockedScheduler = false; export const enableSuspenseCallback = false; export const warnAboutDefaultPropsOnFunctionComponents = false; diff --git a/packages/shared/forks/ReactFeatureFlags.testing.www.js b/packages/shared/forks/ReactFeatureFlags.testing.www.js index c05fbbd2d5..871ffa9cbf 100644 --- a/packages/shared/forks/ReactFeatureFlags.testing.www.js +++ b/packages/shared/forks/ReactFeatureFlags.testing.www.js @@ -26,7 +26,6 @@ export const enableSchedulerDebugging = false; export const enableDeprecatedFlareAPI = true; export const enableFundamentalAPI = false; export const enableScopeAPI = true; -export const enableUseEventAPI = true; export const warnAboutUnmockedScheduler = true; export const enableSuspenseCallback = true; export const warnAboutDefaultPropsOnFunctionComponents = false; diff --git a/packages/shared/forks/ReactFeatureFlags.www.js b/packages/shared/forks/ReactFeatureFlags.www.js index 0d338b25de..b6b1f8a27b 100644 --- a/packages/shared/forks/ReactFeatureFlags.www.js +++ b/packages/shared/forks/ReactFeatureFlags.www.js @@ -60,8 +60,6 @@ export const enableFundamentalAPI = false; export const enableScopeAPI = true; -export const enableUseEventAPI = true; - export const warnAboutUnmockedScheduler = true; export const enableSuspenseCallback = true;