@samitouri / QOS-React-2 / commits / ff431b7fc4

Remove ReactDOM.useEvent and associated types+tests (#18689)

Dominic Gannaway committed Apr 21, 2020 at 16:40 UTC ff431b7fc43ee9022db094a673afbac7088bcfa2
37 files changed +31 -4152
packages/react-art/src/ReactARTHostConfig.js
-16
@@ -493,22 +493,6 @@ export function makeServerId(): OpaqueIDType {
493 throw new Error('Not yet implemented');
494 }
495
496 -export function registerEvent(event: any, rootContainerInstance: any) {
497 - throw new Error('Not yet implemented.');
498 -}
499 -
500 -export function mountEventListener(listener: any) {
501 - throw new Error('Not yet implemented.');
502 -}
503 -
504 -export function unmountEventListener(listener: any) {
505 - throw new Error('Not yet implemented.');
506 -}
507 -
508 -export function validateEventListenerTarget(target: any, listener: any) {
509 - throw new Error('Not yet implemented.');
510 -}
511 -
496 export function beforeActiveInstanceBlur() {
497 // noop
498 }
packages/react-debug-tools/src/ReactDebugHooks.js
-20
@@ -15,7 +15,6 @@ import type {
15 ReactProviderType,
16 ReactEventResponder,
17 ReactEventResponderListener,
18 - ReactScopeMethods,
18 } from 'shared/ReactTypes';
19 import type {
20 Fiber,
@@ -48,14 +47,6 @@ type HookLogEntry = {
47 ...
48 };
49
51 -type ReactDebugListenerMap = {|
52 - clear: () => void,
53 - setListener: (
54 - target: EventTarget | ReactScopeMethods,
55 - callback: ?(SyntheticEvent<EventTarget>) => void,
56 - ) => void,
57 -|};
58 -
50 let hookLog: Array<HookLogEntry> = [];
51
52 // Primitives
@@ -311,16 +302,6 @@ function useTransition(
302 return [callback => {}, false];
303 }
304
314 -const noOp = () => {};
315 -
316 -function useEvent(event: any): ReactDebugListenerMap {
317 - hookLog.push({primitive: 'Event', stackError: new Error(), value: event});
318 - return {
319 - clear: noOp,
320 - setListener: noOp,
321 - };
322 -}
323 -
305 function useDeferredValue<T>(value: T, config: TimeoutConfig | null | void): T {
306 // useDeferredValue() composes multiple hooks internally.
307 // Advance the current hook index the same number of times
@@ -368,7 +349,6 @@ const Dispatcher: DispatcherType = {
349 useTransition,
350 useMutableSource,
351 useDeferredValue,
371 - useEvent,
352 useOpaqueIdentifier,
353 };
354
packages/react-debug-tools/src/__tests__/ReactHooksInspection-test.internal.js
-44
@@ -11,7 +11,6 @@
11 'use strict';
12
13 let React;
14 -let ReactDOM;
14 let ReactDebugTools;
15
16 describe('ReactHooksInspection', () => {
@@ -19,9 +18,7 @@ describe('ReactHooksInspection', () => {
18 jest.resetModules();
19 const ReactFeatureFlags = require('shared/ReactFeatureFlags');
20 ReactFeatureFlags.enableDeprecatedFlareAPI = true;
22 - ReactFeatureFlags.enableUseEventAPI = true;
21 React = require('react');
24 - ReactDOM = require('react-dom');
22 ReactDebugTools = require('react-debug-tools');
23 });
24
@@ -46,45 +43,4 @@ describe('ReactHooksInspection', () => {
43 },
44 ]);
45 });
49 -
50 - // @gate experimental
51 - it('should inspect a simple ReactDOM.useEvent hook', () => {
52 - let clickHandle;
53 - let ref;
54 -
55 - const effect = () => {
56 - clickHandle.setListener(ref.current, () => {});
57 - };
58 -
59 - function Foo(props) {
60 - ref = React.useRef(null);
61 - clickHandle = ReactDOM.unstable_useEvent('click');
62 - React.useEffect(effect);
63 - return <div ref={ref}>Hello world</div>;
64 - }
65 - const tree = ReactDebugTools.inspectHooks(Foo, {});
66 - expect(tree).toEqual([
67 - {
68 - isStateEditable: false,
69 - id: 0,
70 - name: 'Ref',
71 - subHooks: [],
72 - value: null,
73 - },
74 - {
75 - isStateEditable: false,
76 - id: 1,
77 - name: 'Event',
78 - value: {capture: false, passive: undefined, priority: 0, type: 'click'},
79 - subHooks: [],
80 - },
81 - {
82 - isStateEditable: false,
83 - id: 2,
84 - name: 'Effect',
85 - value: effect,
86 - subHooks: [],
87 - },
88 - ]);
89 - });
46 });
packages/react-dom/index.classic.fb.js
-1
@@ -36,5 +36,4 @@ export {
36 unstable_scheduleHydration,
37 unstable_renderSubtreeIntoContainer,
38 unstable_createPortal,
39 - unstable_useEvent,
39 } from './src/client/ReactDOM';
packages/react-dom/index.modern.fb.js
-1
@@ -19,5 +19,4 @@ export {
19 unstable_flushDiscreteUpdates,
20 unstable_flushControlled,
21 unstable_scheduleHydration,
22 - unstable_useEvent,
22 } from './src/client/ReactDOM';
packages/react-dom/src/client/ReactDOM.js
-3
@@ -20,7 +20,6 @@ import {
20 unmountComponentAtNode,
21 } from './ReactDOMLegacy';
22 import {createRoot, createBlockingRoot, isValidContainer} from './ReactDOMRoot';
23 -import {useEvent} from './ReactDOMUseEvent';
23
24 import {
25 batchedEventUpdates,
@@ -211,8 +210,6 @@ export {
210 // Temporary alias since we already shipped React 16 RC with it.
211 // TODO: remove in React 17.
212 unstable_createPortal,
214 - // enableUseEventAPI
215 - useEvent as unstable_useEvent,
213 };
214
215 const foundDevTools = injectIntoDevTools({
packages/react-dom/src/client/ReactDOMComponentTree.js
-17
@@ -15,7 +15,6 @@ import type {
15 SuspenseInstance,
16 Props,
17 } from './ReactDOMHostConfig';
18 -import type {ReactDOMListener} from '../shared/ReactDOMTypes';
18
19 import {
20 HostComponent,
@@ -33,7 +32,6 @@ const randomKey = Math.random()
32 const internalInstanceKey = '__reactFiber$' + randomKey;
33 const internalEventHandlersKey = '__reactEvents$' + randomKey;
34 const internalContainerInstanceKey = '__reactContainer$' + randomKey;
36 -const internalEventListenersKey = '__reactListeners$' + randomKey;
35
36 export function precacheFiberNode(
37 hostInst: Fiber,
@@ -187,18 +185,3 @@ export function updateFiberProps(
185 ): void {
186 (node: any)[internalEventHandlersKey] = props;
187 }
190 -
191 -// This is used for useEvent listeners
192 -export function getListenersFromTarget(
193 - target: EventTarget,
194 -): null | Set<ReactDOMListener> {
195 - return (target: any)[internalEventListenersKey] || null;
196 -}
197 -
198 -// This is used for useEvent listeners
199 -export function initListenersSet(
200 - target: EventTarget,
201 - value: Set<ReactDOMListener>,
202 -): void {
203 - (target: any)[internalEventListenersKey] = value;
204 -}
packages/react-dom/src/client/ReactDOMHostConfig.js
+7 -125
@@ -13,17 +13,12 @@ import type {
13 ReactDOMEventResponder,
14 ReactDOMEventResponderInstance,
15 ReactDOMFundamentalComponentInstance,
16 - ReactDOMListener,
17 - ReactDOMListenerEvent,
18 - ReactDOMListenerMap,
16 } from '../shared/ReactDOMTypes';
20 -import type {ReactScopeMethods} from 'shared/ReactTypes';
17
18 import {
19 precacheFiberNode,
20 updateFiberProps,
21 getClosestInstanceFromNode,
26 - getListenersFromTarget,
22 } from './ReactDOMComponentTree';
23 import {
24 createElement,
@@ -69,32 +64,9 @@ import {
64 enableSuspenseServerRenderer,
65 enableDeprecatedFlareAPI,
66 enableFundamentalAPI,
72 - enableUseEventAPI,
73 - enableScopeAPI,
67 } from 'shared/ReactFeatureFlags';
75 -import {
76 - PLUGIN_EVENT_SYSTEM,
77 - USE_EVENT_SYSTEM,
78 -} from '../events/EventSystemFlags';
79 -import {
80 - isManagedDOMElement,
81 - isValidEventTarget,
82 - listenToTopLevelEvent,
83 - attachListenerToManagedDOMElement,
84 - detachListenerFromManagedDOMElement,
85 - attachTargetEventListener,
86 - detachTargetEventListener,
87 - isReactScope,
88 - attachListenerToReactScope,
89 - detachListenerFromReactScope,
90 -} from '../events/DOMModernPluginEventSystem';
91 -import {getListenerMapForElement} from '../events/DOMEventListenerMap';
68 import {TOP_BEFORE_BLUR, TOP_AFTER_BLUR} from '../events/DOMTopLevelEventTypes';
69
94 -export type ReactListenerEvent = ReactDOMListenerEvent;
95 -export type ReactListenerMap = ReactDOMListenerMap;
96 -export type ReactListener = ReactDOMListener;
97 -
70 export type Type = string;
71 export type Props = {
72 autoFocus?: boolean,
@@ -246,7 +218,7 @@ export function prepareForCommit(containerInfo: Container): Object | null {
218 eventsEnabled = ReactBrowserEventEmitterIsEnabled();
219 selectionInformation = getSelectionInformation();
220 let activeInstance = null;
249 - if (enableDeprecatedFlareAPI || enableUseEventAPI) {
221 + if (enableDeprecatedFlareAPI) {
222 const focusedElem = selectionInformation.focusedElem;
223 if (focusedElem !== null) {
224 activeInstance = getClosestInstanceFromNode(focusedElem);
@@ -257,7 +229,7 @@ export function prepareForCommit(containerInfo: Container): Object | null {
229 }
230
231 export function beforeActiveInstanceBlur(): void {
260 - if (enableDeprecatedFlareAPI || enableUseEventAPI) {
232 + if (enableDeprecatedFlareAPI) {
233 ReactBrowserEventEmitterSetEnabled(true);
234 dispatchBeforeDetachedBlur((selectionInformation: any).focusedElem);
235 ReactBrowserEventEmitterSetEnabled(false);
@@ -265,7 +237,7 @@ export function beforeActiveInstanceBlur(): void {
237 }
238
239 export function afterActiveInstanceBlur(): void {
268 - if (enableDeprecatedFlareAPI || enableUseEventAPI) {
240 + if (enableDeprecatedFlareAPI) {
241 ReactBrowserEventEmitterSetEnabled(true);
242 dispatchAfterDetachedBlur((selectionInformation: any).focusedElem);
243 ReactBrowserEventEmitterSetEnabled(false);
@@ -528,7 +500,7 @@ function createEvent(type: TopLevelType): Event {
500 }
501
502 function dispatchBeforeDetachedBlur(target: HTMLElement): void {
531 - if (enableDeprecatedFlareAPI || enableUseEventAPI) {
503 + if (enableDeprecatedFlareAPI) {
504 const event = createEvent(TOP_BEFORE_BLUR);
505 // Dispatch "beforeblur" directly on the target,
506 // so it gets picked up by the event system and
@@ -538,7 +510,7 @@ function dispatchBeforeDetachedBlur(target: HTMLElement): void {
510 }
511
512 function dispatchAfterDetachedBlur(target: HTMLElement): void {
541 - if (enableDeprecatedFlareAPI || enableUseEventAPI) {
513 + if (enableDeprecatedFlareAPI) {
514 const event = createEvent(TOP_AFTER_BLUR);
515 // So we know what was detached, make the relatedTarget the
516 // detached target on the "afterblur" event.
@@ -550,23 +522,8 @@ function dispatchAfterDetachedBlur(target: HTMLElement): void {
522
523 export function beforeRemoveInstance(
524 instance: Instance | TextInstance | SuspenseInstance,
553 -): void {
554 - if (enableUseEventAPI) {
555 - // It's unfortunate that we have to do this cleanup, but
556 - // it's necessary otherwise we will leak the host instances
557 - // from the useEvent hook instances Map. We call destroy
558 - // on each listener to ensure we properly remove the instance
559 - // from the instances Map. Note: we have this Map so that we
560 - // can properly unmount instances when the function component
561 - // that the hook is attached to gets unmounted.
562 - const listenersSet = getListenersFromTarget(instance);
563 - if (listenersSet !== null) {
564 - const listeners = Array.from(listenersSet);
565 - for (let i = 0; i < listeners.length; i++) {
566 - listeners[i].destroy(instance);
567 - }
568 - }
569 - }
525 +) {
526 + // TODO for ReactDOM.createEventInstance
527 }
528
529 export function removeChild(
@@ -1141,78 +1098,3 @@ export function makeOpaqueHydratingObject(
1098 valueOf: attemptToReadValue,
1099 };
1100 }
1144 -
1145 -export function registerEvent(
1146 - event: ReactDOMListenerEvent,
1147 - rootContainerInstance: Container,
1148 -): void {
1149 - const {passive, priority, type} = event;
1150 - const listenerMap = getListenerMapForElement(rootContainerInstance);
1151 - // Add the event listener to the target container (falling back to
1152 - // the target if we didn't find one).
1153 - listenToTopLevelEvent(
1154 - type,
1155 - rootContainerInstance,
1156 - listenerMap,
1157 - PLUGIN_EVENT_SYSTEM | USE_EVENT_SYSTEM,
1158 - passive,
1159 - priority,
1160 - );
1161 -}
1162 -
1163 -export function mountEventListener(listener: ReactDOMListener): void {
1164 - if (enableUseEventAPI) {
1165 - const {target} = listener;
1166 - if (isManagedDOMElement(target)) {
1167 - attachListenerToManagedDOMElement(listener);
1168 - } else if (enableScopeAPI && isReactScope(target)) {
1169 - attachListenerToReactScope(listener);
1170 - } else {
1171 - attachTargetEventListener(listener);
1172 - }
1173 - }
1174 -}
1175 -
1176 -export function unmountEventListener(listener: ReactDOMListener): void {
1177 - if (enableUseEventAPI) {
1178 - const {target} = listener;
1179 - if (isManagedDOMElement(target)) {
1180 - detachListenerFromManagedDOMElement(listener);
1181 - } else if (enableScopeAPI && isReactScope(target)) {
1182 - detachListenerFromReactScope(listener);
1183 - } else {
1184 - detachTargetEventListener(listener);
1185 - }
1186 - }
1187 -}
1188 -
1189 -export function validateEventListenerTarget(
1190 - target: EventTarget | ReactScopeMethods,
1191 - listener: ?(SyntheticEvent<EventTarget>) => void,
1192 -): boolean {
1193 - if (enableUseEventAPI) {
1194 - if (
1195 - target != null &&
1196 - (isManagedDOMElement(target) ||
1197 - isValidEventTarget(target) ||
1198 - isReactScope(target))
1199 - ) {
1200 - if (listener == null || typeof listener === 'function') {
1201 - return true;
1202 - }
1203 - if (__DEV__) {
1204 - console.warn(
1205 - 'Event listener method setListener() from useEvent() hook requires the second argument' +
1206 - ' to be either a valid function callback or null/undefined.',
1207 - );
1208 - }
1209 - }
1210 - if (__DEV__) {
1211 - console.warn(
1212 - 'Event listener method setListener() from useEvent() hook requires the first argument to be ' +
1213 - 'a valid DOM EventTarget. If using a ref, ensure the current value is not null.',
1214 - );
1215 - }
1216 - }
1217 - return false;
1218 -}
packages/react-dom/src/client/ReactDOMUseEvent.js deleted
-79
@@ -1,79 +0,0 @@
1 -/**
2 - * Copyright (c) Facebook, Inc. and its affiliates.
3 - *
4 - * This source code is licensed under the MIT license found in the
5 - * LICENSE file in the root directory of this source tree.
6 - *
7 - * @flow
8 - */
9 -
10 -import type {DOMTopLevelEventType} from 'legacy-events/TopLevelEventTypes';
11 -import type {EventPriority} from 'shared/ReactTypes';
12 -import type {
13 - ReactDOMListenerEvent,
14 - ReactDOMListenerMap,
15 -} from '../shared/ReactDOMTypes';
16 -
17 -import ReactSharedInternals from 'shared/ReactSharedInternals';
18 -import invariant from 'shared/invariant';
19 -
20 -import {getEventPriorityForListenerSystem} from '../events/DOMEventProperties';
21 -
22 -type EventOptions = {|
23 - capture?: boolean,
24 - passive?: boolean,
25 - priority?: EventPriority,
26 -|};
27 -
28 -const {ReactCurrentDispatcher} = ReactSharedInternals;
29 -
30 -function resolveDispatcher() {
31 - const dispatcher = ReactCurrentDispatcher.current;
32 - invariant(
33 - dispatcher !== null,
34 - 'Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for' +
35 - ' one of the following reasons:\n' +
36 - '1. You might have mismatching versions of React and the renderer (such as React DOM)\n' +
37 - '2. You might be breaking the Rules of Hooks\n' +
38 - '3. You might have more than one copy of React in the same app\n' +
39 - 'See https://fb.me/react-invalid-hook-call for tips about how to debug and fix this problem.',
40 - );
41 - return dispatcher;
42 -}
43 -
44 -export function useEvent(
45 - type: string,
46 - options?: EventOptions,
47 -): ReactDOMListenerMap {
48 - const topLevelType = ((type: any): DOMTopLevelEventType);
49 - const dispatcher = resolveDispatcher();
50 - let capture = false;
51 - let passive = undefined; // Undefined means to use the browser default
52 - let priority;
53 -
54 - if (options != null) {
55 - const optionsCapture = options.capture;
56 - const optionsPassive = options.passive;
57 - const optionsPriority = options.priority;
58 -
59 - if (typeof optionsCapture === 'boolean') {
60 - capture = optionsCapture;
61 - }
62 - if (typeof optionsPassive === 'boolean') {
63 - passive = optionsPassive;
64 - }
65 - if (typeof optionsPriority === 'number') {
66 - priority = optionsPriority;
67 - }
68 - }
69 - if (priority === undefined) {
70 - priority = getEventPriorityForListenerSystem(topLevelType);
71 - }
72 - const event: ReactDOMListenerEvent = {
73 - capture,
74 - passive,
75 - priority,
76 - type: topLevelType,
77 - };
78 - return dispatcher.useEvent(event);
79 -}
packages/react-dom/src/events/DOMEventProperties.js
-8
@@ -23,7 +23,6 @@ import {
23 UserBlockingEvent,
24 ContinuousEvent,
25 } from 'shared/ReactTypes';
26 -import {enableUseEventAPI} from 'shared/ReactFeatureFlags';
26
27 // Needed for SimpleEventPlugin, rather than
28 // do it in two places, which duplicates logic
@@ -96,13 +95,6 @@ const otherDiscreteEvents = [
95 DOMTopLevelEventTypes.TOP_COMPOSITION_UPDATE,
96 ];
97
99 -if (enableUseEventAPI) {
100 - otherDiscreteEvents.push(
101 - DOMTopLevelEventTypes.TOP_BEFORE_BLUR,
102 - DOMTopLevelEventTypes.TOP_AFTER_BLUR,
103 - );
104 -}
105 -
98 // prettier-ignore
99 const userBlockingPairsForSimpleEventPlugin = [
100 DOMTopLevelEventTypes.TOP_DRAG, 'drag',
packages/react-dom/src/events/DOMModernPluginEventSystem.js
+4 -347
@@ -17,11 +17,7 @@ import type {EventSystemFlags} from './EventSystemFlags';
17 import type {EventPriority, ReactScopeMethods} from 'shared/ReactTypes';
18 import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
19 import type {PluginModule} from 'legacy-events/PluginModuleType';
20 -import type {
21 - ReactSyntheticEvent,
22 - CustomDispatchConfig,
23 -} from 'legacy-events/ReactSyntheticEventType';
24 -import type {ReactDOMListener} from '../shared/ReactDOMTypes';
20 +import type {ReactSyntheticEvent} from 'legacy-events/ReactSyntheticEventType';
21
22 import {registrationNameDependencies} from 'legacy-events/EventPluginRegistry';
23 import {plugins} from 'legacy-events/EventPluginRegistry';
@@ -36,7 +32,6 @@ import {
32 import {
33 HostRoot,
34 HostPortal,
39 - ScopeComponent,
35 HostComponent,
36 } from 'react-reconciler/src/ReactWorkTags';
37
@@ -80,25 +75,14 @@ import {
75 TOP_PROGRESS,
76 TOP_PLAYING,
77 TOP_CLICK,
83 - TOP_BEFORE_BLUR,
84 - TOP_AFTER_BLUR,
78 TOP_SELECTION_CHANGE,
79 } from './DOMTopLevelEventTypes';
87 -import {
88 - getClosestInstanceFromNode,
89 - getListenersFromTarget,
90 - initListenersSet,
91 -} from '../client/ReactDOMComponentTree';
80 +import {getClosestInstanceFromNode} from '../client/ReactDOMComponentTree';
81 import {COMMENT_NODE} from '../shared/HTMLNodeType';
93 -import {topLevelEventsToDispatchConfig} from './DOMEventProperties';
82 import {batchedEventUpdates} from './ReactDOMUpdateBatching';
83 import getListener from './getListener';
84
97 -import {
98 - enableLegacyFBSupport,
99 - enableUseEventAPI,
100 - enableScopeAPI,
101 -} from 'shared/ReactFeatureFlags';
85 +import {enableLegacyFBSupport} from 'shared/ReactFeatureFlags';
86 import {
87 invokeGuardedCallbackAndCatchFirstError,
88 rethrowCaughtError,
@@ -140,44 +124,8 @@ const capturePhaseEvents = new Set([
124 TOP_WAITING,
125 ]);
126
143 -if (enableUseEventAPI) {
144 - capturePhaseEvents.add(TOP_BEFORE_BLUR);
145 - capturePhaseEvents.add(TOP_AFTER_BLUR);
146 -}
147 -
148 -const emptyDispatchConfigForCustomEvents: CustomDispatchConfig = {
149 - customEvent: true,
150 - phasedRegistrationNames: {
151 - bubbled: null,
152 - captured: null,
153 - },
154 -};
155 -
127 const isArray = Array.isArray;
128
158 -// TODO: we should remove the FlowFixMes and the casting to figure out how to make
159 -// these patterns work properly.
160 -// $FlowFixMe: Flow struggles with this pattern, so we also have to cast it.
161 -const PossiblyWeakMap = ((typeof WeakMap === 'function' ? WeakMap : Map): any);
162 -
163 -// $FlowFixMe: Flow cannot handle polymorphic WeakMaps
164 -const eventTargetEventListenerStore: WeakMap<
165 - EventTarget,
166 - Map<
167 - DOMTopLevelEventType,
168 - {bubbled: Set<ReactDOMListener>, captured: Set<ReactDOMListener>},
169 - >,
170 -> = new PossiblyWeakMap();
171 -
172 -// $FlowFixMe: Flow cannot handle polymorphic WeakMaps
173 -const reactScopeListenerStore: WeakMap<
174 - ReactScopeMethods,
175 - Map<
176 - DOMTopLevelEventType,
177 - {bubbled: Set<ReactDOMListener>, captured: Set<ReactDOMListener>},
178 - >,
179 -> = new PossiblyWeakMap();
180 -
129 function executeDispatch(
130 event: ReactSyntheticEvent,
131 listener: Function,
@@ -514,183 +462,7 @@ export function dispatchEventForPluginEventSystem(
462 );
463 }
464
517 -function getNearestRootOrPortalContainer(instance: Element): Element {
518 - let node = getClosestInstanceFromNode(instance);
519 - while (node !== null) {
520 - const tag = node.tag;
521 - // Once we encounter a host container or root container
522 - // we can return their DOM instance.
523 - if (tag === HostRoot || tag === HostPortal) {
524 - return node.stateNode.containerInfo;
525 - }
526 - node = node.return;
527 - }
528 - return instance;
529 -}
530 -
531 -function addEventTypeToDispatchConfig(type: DOMTopLevelEventType): void {
532 - const dispatchConfig = topLevelEventsToDispatchConfig.get(type);
533 - // If we don't have a dispatchConfig, then we're dealing with
534 - // an event type that React does not know about (i.e. a custom event).
535 - // We need to register an event config for this or the SimpleEventPlugin
536 - // will not appropriately provide a SyntheticEvent, so we use out empty
537 - // dispatch config for custom events.
538 - if (dispatchConfig === undefined) {
539 - topLevelEventsToDispatchConfig.set(
540 - type,
541 - emptyDispatchConfigForCustomEvents,
542 - );
543 - }
544 -}
545 -
546 -export function attachListenerToManagedDOMElement(
547 - listener: ReactDOMListener,
548 -): void {
549 - const {event, target} = listener;
550 - const {passive, priority, type} = event;
551 -
552 - const managedTargetElement = ((target: any): Element);
553 - const containerEventTarget = getNearestRootOrPortalContainer(
554 - managedTargetElement,
555 - );
556 - const listenerMap = getListenerMapForElement(containerEventTarget);
557 - // Add the event listener to the target container (falling back to
558 - // the target if we didn't find one).
559 - listenToTopLevelEvent(
560 - type,
561 - containerEventTarget,
562 - listenerMap,
563 - PLUGIN_EVENT_SYSTEM | USE_EVENT_SYSTEM,
564 - passive,
565 - priority,
566 - );
567 - // Get the internal listeners Set from the target instance.
568 - let listeners = getListenersFromTarget(managedTargetElement);
569 - // If we don't have any listeners, then we need to init them.
570 - if (listeners === null) {
571 - listeners = new Set();
572 - initListenersSet(managedTargetElement, listeners);
573 - }
574 - // Add our listener to the listeners Set.
575 - listeners.add(listener);
576 - // Finally, add the event to our known event types list.
577 - addEventTypeToDispatchConfig(type);
578 -}
579 -
580 -export function detachListenerFromManagedDOMElement(
581 - listener: ReactDOMListener,
582 -): void {
583 - const {target} = listener;
584 - const managedTargetElement = ((target: any): Element);
585 - // Get the internal listeners Set from the target instance.
586 - const listeners = getListenersFromTarget(managedTargetElement);
587 - if (listeners !== null) {
588 - // Remove out listener from the listeners Set.
589 - listeners.delete(listener);
590 - }
591 -}
592 -
593 -export function attachTargetEventListener(listener: ReactDOMListener): void {
594 - const {event, target} = listener;
595 - const {capture, passive, priority, type} = event;
596 - const eventTarget = ((target: any): EventTarget);
597 - const listenerMap = getListenerMapForElement(eventTarget);
598 - // Add the event listener to the TargetEvent object.
599 - listenToTopLevelEvent(
600 - type,
601 - eventTarget,
602 - listenerMap,
603 - PLUGIN_EVENT_SYSTEM | USE_EVENT_SYSTEM | IS_TARGET_PHASE_ONLY,
604 - passive,
605 - priority,
606 - capture,
607 - );
608 - let eventTypeMap = eventTargetEventListenerStore.get(eventTarget);
609 - if (eventTypeMap === undefined) {
610 - eventTypeMap = new Map();
611 - eventTargetEventListenerStore.set(eventTarget, eventTypeMap);
612 - }
613 - // Get the listeners by the event type
614 - let listeners = eventTypeMap.get(type);
615 - if (listeners === undefined) {
616 - listeners = {captured: new Set(), bubbled: new Set()};
617 - eventTypeMap.set(type, listeners);
618 - }
619 - // Add our listener to the listeners Set.
620 - if (capture) {
621 - listeners.captured.add(listener);
622 - } else {
623 - listeners.bubbled.add(listener);
624 - }
625 - // Finally, add the event to our known event types list.
626 - addEventTypeToDispatchConfig(type);
627 -}
628 -
629 -export function detachTargetEventListener(listener: ReactDOMListener): void {
630 - const {event, target} = listener;
631 - const {capture, type} = event;
632 - const validEventTarget = ((target: any): EventTarget);
633 - const eventTypeMap = eventTargetEventListenerStore.get(validEventTarget);
634 - if (eventTypeMap !== undefined) {
635 - const listeners = eventTypeMap.get(type);
636 - if (listeners !== undefined) {
637 - // Remove out listener from the listeners Set.
638 - if (capture) {
639 - listeners.captured.delete(listener);
640 - } else {
641 - listeners.bubbled.delete(listener);
642 - }
643 - }
644 - }
645 -}
646 -
647 -export function attachListenerToReactScope(listener: ReactDOMListener): void {
648 - const {event, target} = listener;
649 - const {capture, type} = event;
650 - const reactScope = ((target: any): ReactScopeMethods);
651 - let eventTypeMap = reactScopeListenerStore.get(reactScope);
652 - if (eventTypeMap === undefined) {
653 - eventTypeMap = new Map();
654 - reactScopeListenerStore.set(reactScope, eventTypeMap);
655 - }
656 - // Get the listeners by the event type
657 - let listeners = eventTypeMap.get(type);
658 - if (listeners === undefined) {
659 - listeners = {captured: new Set(), bubbled: new Set()};
660 - eventTypeMap.set(type, listeners);
661 - }
662 - // Add our listener to the listeners Set.
663 - if (capture) {
664 - listeners.captured.add(listener);
665 - } else {
666 - listeners.bubbled.add(listener);
667 - }
668 - // Finally, add the event to our known event types list.
669 - addEventTypeToDispatchConfig(type);
670 -}
671 -
672 -export function detachListenerFromReactScope(listener: ReactDOMListener): void {
673 - const {event, target} = listener;
674 - const {capture, type} = event;
675 - const reactScope = ((target: any): ReactScopeMethods);
676 - const eventTypeMap = reactScopeListenerStore.get(reactScope);
677 - if (eventTypeMap !== undefined) {
678 - const listeners = eventTypeMap.get(type);
679 - if (listeners !== undefined) {
680 - // Remove out listener from the listeners Set.
681 - if (capture) {
682 - listeners.captured.delete(listener);
683 - } else {
684 - listeners.bubbled.delete(listener);
685 - }
686 - }
687 - }
688 -}
689 -
690 -export function accumulateTwoPhaseListeners(
691 - event: ReactSyntheticEvent,
692 - accumulateUseEventListeners?: boolean,
693 -): void {
465 +export function accumulateTwoPhaseListeners(event: ReactSyntheticEvent): void {
466 const phasedRegistrationNames = event.dispatchConfig.phasedRegistrationNames;
467 const dispatchListeners = [];
468 const dispatchInstances: Array<Fiber | null> = [];
@@ -701,7 +473,6 @@ export function accumulateTwoPhaseListeners(
473 // usual two phase accumulation using the React fiber tree to pick up
474 // all relevant useEvent and on* prop events.
475 let instance = event._targetInst;
704 - let lastHostComponent = null;
476
477 // Accumulate all instances and listeners via the target -> root path.
478 while (instance !== null) {
@@ -709,35 +480,6 @@ export function accumulateTwoPhaseListeners(
480 // Handle listeners that are on HostComponents (i.e. <div>)
481 if (tag === HostComponent && stateNode !== null) {
482 const currentTarget = stateNode;
712 - lastHostComponent = currentTarget;
713 - // For useEvent listenrs
714 - if (enableUseEventAPI && accumulateUseEventListeners) {
715 - // useEvent event listeners
716 - const targetType = event.type;
717 - const listeners = getListenersFromTarget(currentTarget);
718 -
719 - if (listeners !== null) {
720 - const listenersArr = Array.from(listeners);
721 - for (let i = 0; i < listenersArr.length; i++) {
722 - const listener = listenersArr[i];
723 - const {
724 - callback,
725 - event: {capture, type},
726 - } = listener;
727 - if (type === targetType) {
728 - if (capture === true) {
729 - dispatchListeners.unshift(callback);
730 - dispatchInstances.unshift(instance);
731 - dispatchCurrentTargets.unshift(currentTarget);
732 - } else {
733 - dispatchListeners.push(callback);
734 - dispatchInstances.push(instance);
735 - dispatchCurrentTargets.push(currentTarget);
736 - }
737 - }
738 - }
739 - }
740 - }
483 // Standard React on* listeners, i.e. onClick prop
484 if (captured !== null) {
485 const captureListener = getListener(instance, captured);
@@ -760,40 +502,6 @@ export function accumulateTwoPhaseListeners(
502 }
503 }
504 }
763 - if (
764 - enableUseEventAPI &&
765 - enableScopeAPI &&
766 - accumulateUseEventListeners &&
767 - tag === ScopeComponent &&
768 - lastHostComponent !== null
769 - ) {
770 - const reactScope = stateNode.methods;
771 - const eventTypeMap = reactScopeListenerStore.get(reactScope);
772 - if (eventTypeMap !== undefined) {
773 - const type = ((event.type: any): DOMTopLevelEventType);
774 - const listeners = eventTypeMap.get(type);
775 - if (listeners !== undefined) {
776 - const captureListeners = Array.from(listeners.captured);
777 - const bubbleListeners = Array.from(listeners.bubbled);
778 - const lastCurrentTarget = ((lastHostComponent: any): Element);
779 -
780 - for (let i = 0; i < captureListeners.length; i++) {
781 - const listener = captureListeners[i];
782 - const {callback} = listener;
783 - dispatchListeners.unshift(callback);
784 - dispatchInstances.unshift(instance);
785 - dispatchCurrentTargets.unshift(lastCurrentTarget);
786 - }
787 - for (let i = 0; i < bubbleListeners.length; i++) {
788 - const listener = bubbleListeners[i];
789 - const {callback} = listener;
790 - dispatchListeners.push(callback);
791 - dispatchInstances.push(instance);
792 - dispatchCurrentTargets.push(lastCurrentTarget);
793 - }
794 - }
795 - }
796 - }
505 instance = instance.return;
506 }
507
@@ -806,57 +514,6 @@ export function accumulateTwoPhaseListeners(
514 }
515 }
516
809 -export function accumulateEventTargetListeners(
810 - event: ReactSyntheticEvent,
811 - currentTarget: EventTarget,
812 -): void {
813 - const dispatchListeners = [];
814 - const dispatchInstances: Array<Fiber | null> = [];
815 - const dispatchCurrentTargets = [];
816 -
817 - const eventTypeMap = eventTargetEventListenerStore.get(currentTarget);
818 - if (eventTypeMap !== undefined) {
819 - const type = ((event.type: any): DOMTopLevelEventType);
820 - const listeners = eventTypeMap.get(type);
821 - if (listeners !== undefined) {
822 - const isCapturePhase = (event: any).eventPhase === 1;
823 -
824 - if (isCapturePhase) {
825 - const captureListeners = Array.from(listeners.captured);
826 -
827 - for (let i = captureListeners.length - 1; i >= 0; i--) {
828 - const listener = captureListeners[i];
829 - const {callback} = listener;
830 - dispatchListeners.push(callback);
831 - // EventTarget listeners do not have instances, as there
832 - // is no backing Fiber instance for them (window, document etc).
833 - dispatchInstances.push(null);
834 - dispatchCurrentTargets.push(currentTarget);
835 - }
836 - } else {
837 - const bubbleListeners = Array.from(listeners.bubbled);
838 -
839 - for (let i = 0; i < bubbleListeners.length; i++) {
840 - const listener = bubbleListeners[i];
841 - const {callback} = listener;
842 - dispatchListeners.push(callback);
843 - // EventTarget listeners do not have instances, as there
844 - // is no backing Fiber instance for them (window, document etc).
845 - dispatchInstances.push(null);
846 - dispatchCurrentTargets.push(currentTarget);
847 - }
848 - }
849 - }
850 - }
851 - // To prevent allocation to the event unless we actually
852 - // have listeners we check the length of one of the arrays.
853 - if (dispatchListeners.length > 0) {
854 - event._dispatchListeners = dispatchListeners;
855 - event._dispatchInstances = dispatchInstances;
856 - event._dispatchCurrentTargets = dispatchCurrentTargets;
857 - }
858 -}
859 -
517 function getParent(inst: Fiber | null): Fiber | null {
518 if (inst === null) {
519 return null;
packages/react-dom/src/events/ReactDOMEventListener.js
+10 -32
@@ -44,7 +44,6 @@ import {
44 addEventBubbleListener,
45 addEventCaptureListener,
46 addEventCaptureListenerWithPassiveFlag,
47 - addEventBubbleListenerWithPassiveFlag,
47 removeEventListener,
48 } from './EventListener';
49 import getEventTarget from './getEventTarget';
@@ -56,7 +55,6 @@ import {
55 enableDeprecatedFlareAPI,
56 enableModernEventSystem,
57 enableLegacyFBSupport,
59 - enableUseEventAPI,
58 } from 'shared/ReactFeatureFlags';
59 import {
60 UserBlockingEvent,
@@ -200,37 +198,17 @@ export function addTrappedEventListener(
198 };
199 }
200 if (capture) {
203 - if (enableUseEventAPI && passive !== undefined) {
204 - // This is only used with passive is either true or false.
205 - unsubscribeListener = addEventCaptureListenerWithPassiveFlag(
206 - targetContainer,
207 - rawEventName,
208 - listener,
209 - passive,
210 - );
211 - } else {
212 - unsubscribeListener = addEventCaptureListener(
213 - targetContainer,
214 - rawEventName,
215 - listener,
216 - );
217 - }
201 + unsubscribeListener = addEventCaptureListener(
202 + targetContainer,
203 + rawEventName,
204 + listener,
205 + );
206 } else {
219 - if (enableUseEventAPI && passive !== undefined) {
220 - // This is only used with passive is either true or false.
221 - unsubscribeListener = addEventBubbleListenerWithPassiveFlag(
222 - targetContainer,
223 - rawEventName,
224 - listener,
225 - passive,
226 - );
227 - } else {
228 - unsubscribeListener = addEventBubbleListener(
229 - targetContainer,
230 - rawEventName,
231 - listener,
232 - );
233 - }
207 + unsubscribeListener = addEventBubbleListener(
208 + targetContainer,
209 + rawEventName,
210 + listener,
211 + );
212 }
213 return unsubscribeListener;
214 }
packages/react-dom/src/events/__tests__/DOMModernPluginEventSystem-test.internal.js
-1549
@@ -9,13 +9,10 @@
9
10 'use strict';
11
12 -import {createEventTarget} from 'dom-event-testing-library';
13 -
12 let React;
13 let ReactFeatureFlags;
14 let ReactDOM;
15 let ReactDOMServer;
18 -let ReactTestUtils;
16 let Scheduler;
17
18 function dispatchEvent(element, type) {
@@ -67,7 +64,6 @@ describe('DOMModernPluginEventSystem', () => {
64 ReactDOM = require('react-dom');
65 Scheduler = require('scheduler');
66 ReactDOMServer = require('react-dom/server');
70 - ReactTestUtils = require('react-dom/test-utils');
67 container = document.createElement('div');
68 document.body.appendChild(container);
69 startNativeEventListenerClearDown();
@@ -1082,1551 +1078,6 @@ describe('DOMModernPluginEventSystem', () => {
1078 ]);
1079 }
1080 });
1085 -
1086 - describe('ReactDOM.useEvent', () => {
1087 - beforeEach(() => {
1088 - jest.resetModules();
1089 - ReactFeatureFlags = require('shared/ReactFeatureFlags');
1090 - ReactFeatureFlags.enableModernEventSystem = true;
1091 - ReactFeatureFlags.enableUseEventAPI = true;
1092 -
1093 - React = require('react');
1094 - ReactDOM = require('react-dom');
1095 - Scheduler = require('scheduler');
1096 - ReactDOMServer = require('react-dom/server');
1097 - ReactTestUtils = require('react-dom/test-utils');
1098 - });
1099 -
1100 - // @gate experimental
1101 - it('should create the same event listener map', () => {
1102 - const listenerMaps = [];
1103 -
1104 - function Test() {
1105 - const listenerMap = ReactDOM.unstable_useEvent('click');
1106 -
1107 - listenerMaps.push(listenerMap);
1108 -
1109 - return <div />;
1110 - }
1111 -
1112 - ReactDOM.render(<Test />, container);
1113 - ReactDOM.render(<Test />, container);
1114 - expect(listenerMaps.length).toEqual(2);
1115 - expect(listenerMaps[0]).toEqual(listenerMaps[1]);
1116 - });
1117 -
1118 - // @gate experimental
1119 - it('can render correctly with the ReactDOMServer', () => {
1120 - const clickEvent = jest.fn();
1121 -
1122 - function Test() {
1123 - const divRef = React.useRef(null);
1124 - const click = ReactDOM.unstable_useEvent('click');
1125 -
1126 - React.useEffect(() => {
1127 - click.setListener(divRef.current, clickEvent);
1128 - });
1129 -
1130 - return <div ref={divRef}>Hello world</div>;
1131 - }
1132 - const output = ReactDOMServer.renderToString(<Test />);
1133 - expect(output).toBe(`<div data-reactroot="">Hello world</div>`);
1134 - });
1135 -
1136 - // @gate experimental
1137 - it('can render correctly with the ReactDOMServer hydration', () => {
1138 - const clickEvent = jest.fn();
1139 - const spanRef = React.createRef();
1140 -
1141 - function Test() {
1142 - const click = ReactDOM.unstable_useEvent('click');
1143 -
1144 - React.useEffect(() => {
1145 - click.setListener(spanRef.current, clickEvent);
1146 - });
1147 -
1148 - return (
1149 - <div>
1150 - <span ref={spanRef}>Hello world</span>
1151 - </div>
1152 - );
1153 - }
1154 - const output = ReactDOMServer.renderToString(<Test />);
1155 - expect(output).toBe(
1156 - `<div data-reactroot=""><span>Hello world</span></div>`,
1157 - );
1158 - container.innerHTML = output;
1159 - ReactDOM.hydrate(<Test />, container);
1160 - Scheduler.unstable_flushAll();
1161 - dispatchClickEvent(spanRef.current);
1162 - expect(clickEvent).toHaveBeenCalledTimes(1);
1163 - });
1164 -
1165 - // @gate experimental
1166 - it('should correctly work for a basic "click" listener', () => {
1167 - let log = [];
1168 - const clickEvent = jest.fn(event => {
1169 - log.push({
1170 - eventPhase: event.eventPhase,
1171 - type: event.type,
1172 - currentTarget: event.currentTarget,
1173 - target: event.target,
1174 - });
1175 - });
1176 - const divRef = React.createRef();
1177 - const buttonRef = React.createRef();
1178 -
1179 - function Test() {
1180 - const click = ReactDOM.unstable_useEvent('click');
1181 -
1182 - React.useEffect(() => {
1183 - click.setListener(buttonRef.current, clickEvent);
1184 - });
1185 -
1186 - return (
1187 - <button ref={buttonRef}>
1188 - <div ref={divRef}>Click me!</div>
1189 - </button>
1190 - );
1191 - }
1192 -
1193 - ReactDOM.render(<Test />, container);
1194 - Scheduler.unstable_flushAll();
1195 -
1196 - expect(container.innerHTML).toBe(
1197 - '<button><div>Click me!</div></button>',
1198 - );
1199 -
1200 - // Clicking the button should trigger the event callback
1201 - let divElement = divRef.current;
1202 - dispatchClickEvent(divElement);
1203 - expect(log).toEqual([
1204 - {
1205 - eventPhase: 3,
1206 - type: 'click',
1207 - currentTarget: buttonRef.current,
1208 - target: divRef.current,
1209 - },
1210 - ]);
1211 - expect(clickEvent).toBeCalledTimes(1);
1212 -
1213 - // Unmounting the container and clicking should not work
1214 - ReactDOM.render(null, container);
1215 - Scheduler.unstable_flushAll();
1216 -
1217 - dispatchClickEvent(divElement);
1218 - expect(clickEvent).toBeCalledTimes(1);
1219 -
1220 - // Re-rendering the container and clicking should work
1221 - ReactDOM.render(<Test />, container);
1222 - Scheduler.unstable_flushAll();
1223 -
1224 - divElement = divRef.current;
1225 - dispatchClickEvent(divElement);
1226 - expect(clickEvent).toBeCalledTimes(2);
1227 -
1228 - log = [];
1229 -
1230 - // Clicking the button should also work
1231 - const buttonElement = buttonRef.current;
1232 - dispatchClickEvent(buttonElement);
1233 - expect(log).toEqual([
1234 - {
1235 - eventPhase: 3,
1236 - type: 'click',
1237 - currentTarget: buttonRef.current,
1238 - target: buttonRef.current,
1239 - },
1240 - ]);
1241 -
1242 - function Test2({clickEvent2}) {
1243 - const click = ReactDOM.unstable_useEvent('click', clickEvent2);
1244 -
1245 - React.useEffect(() => {
1246 - click.setListener(buttonRef.current, clickEvent2);
1247 - });
1248 -
1249 - return (
1250 - <button ref={buttonRef}>
1251 - <div ref={divRef}>Click me!</div>
1252 - </button>
1253 - );
1254 - }
1255 -
1256 - let clickEvent2 = jest.fn();
1257 - ReactDOM.render(<Test2 clickEvent2={clickEvent2} />, container);
1258 - Scheduler.unstable_flushAll();
1259 -
1260 - divElement = divRef.current;
1261 - dispatchClickEvent(divElement);
1262 - expect(clickEvent2).toBeCalledTimes(1);
1263 -
1264 - // Reset the function we pass in, so it's different
1265 - clickEvent2 = jest.fn();
1266 - ReactDOM.render(<Test2 clickEvent2={clickEvent2} />, container);
1267 - Scheduler.unstable_flushAll();
1268 -
1269 - divElement = divRef.current;
1270 - dispatchClickEvent(divElement);
1271 - expect(clickEvent2).toBeCalledTimes(1);
1272 - });
1273 -
1274 - // @gate experimental
1275 - it('should correctly work for setting and clearing a basic "click" listener', () => {
1276 - const clickEvent = jest.fn();
1277 - const divRef = React.createRef();
1278 - const buttonRef = React.createRef();
1279 -
1280 - function Test({off}) {
1281 - const click = ReactDOM.unstable_useEvent('click');
1282 -
1283 - React.useEffect(() => {
1284 - click.setListener(buttonRef.current, clickEvent);
1285 - });
1286 -
1287 - React.useEffect(() => {
1288 - if (off) {
1289 - click.setListener(buttonRef.current, null);
1290 - }
1291 - }, [off]);
1292 -
1293 - return (
1294 - <button ref={buttonRef}>
1295 - <div ref={divRef}>Click me!</div>
1296 - </button>
1297 - );
1298 - }
1299 -
1300 - ReactDOM.render(<Test off={false} />, container);
1301 - Scheduler.unstable_flushAll();
1302 -
1303 - let divElement = divRef.current;
1304 - dispatchClickEvent(divElement);
1305 - expect(clickEvent).toBeCalledTimes(1);
1306 -
1307 - // The listener should get unmounted in the second effect
1308 - ReactDOM.render(<Test off={true} />, container);
1309 - Scheduler.unstable_flushAll();
1310 -
1311 - clickEvent.mockClear();
1312 -
1313 - divElement = divRef.current;
1314 - dispatchClickEvent(divElement);
1315 - expect(clickEvent).toBeCalledTimes(0);
1316 - });
1317 -
1318 - // @gate experimental
1319 - it('should handle the target being a text node', () => {
1320 - const clickEvent = jest.fn();
1321 - const buttonRef = React.createRef();
1322 -
1323 - function Test() {
1324 - const click = ReactDOM.unstable_useEvent('click');
1325 -
1326 - React.useEffect(() => {
1327 - click.setListener(buttonRef.current, clickEvent);
1328 - });
1329 -
1330 - return <button ref={buttonRef}>Click me!</button>;
1331 - }
1332 -
1333 - ReactDOM.render(<Test />, container);
1334 - Scheduler.unstable_flushAll();
1335 -
1336 - const textNode = buttonRef.current.firstChild;
1337 - dispatchClickEvent(textNode);
1338 - expect(clickEvent).toBeCalledTimes(1);
1339 - });
1340 -
1341 - // @gate experimental
1342 - it('handle propagation of click events', () => {
1343 - const buttonRef = React.createRef();
1344 - const divRef = React.createRef();
1345 - const log = [];
1346 - const onClick = jest.fn(e => log.push(['bubble', e.currentTarget]));
1347 - const onClickCapture = jest.fn(e =>
1348 - log.push(['capture', e.currentTarget]),
1349 - );
1350 -
1351 - function Test() {
1352 - const click = ReactDOM.unstable_useEvent('click');
1353 - const clickCapture = ReactDOM.unstable_useEvent('click', {
1354 - capture: true,
1355 - });
1356 -
1357 - React.useEffect(() => {
1358 - click.setListener(buttonRef.current, onClick);
1359 - clickCapture.setListener(buttonRef.current, onClickCapture);
1360 - click.setListener(divRef.current, onClick);
1361 - clickCapture.setListener(divRef.current, onClickCapture);
1362 - });
1363 -
1364 - return (
1365 - <button ref={buttonRef}>
1366 - <div ref={divRef}>Click me!</div>
1367 - </button>
1368 - );
1369 - }
1370 -
1371 - ReactDOM.render(<Test />, container);
1372 - Scheduler.unstable_flushAll();
1373 -
1374 - const buttonElement = buttonRef.current;
1375 - dispatchClickEvent(buttonElement);
1376 - expect(onClick).toHaveBeenCalledTimes(1);
1377 - expect(onClickCapture).toHaveBeenCalledTimes(1);
1378 - expect(log[0]).toEqual(['capture', buttonElement]);
1379 - expect(log[1]).toEqual(['bubble', buttonElement]);
1380 -
1381 - log.length = 0;
1382 - onClick.mockClear();
1383 - onClickCapture.mockClear();
1384 -
1385 - const divElement = divRef.current;
1386 - dispatchClickEvent(divElement);
1387 - expect(onClick).toHaveBeenCalledTimes(2);
1388 - expect(onClickCapture).toHaveBeenCalledTimes(2);
1389 - expect(log[0]).toEqual(['capture', buttonElement]);
1390 - expect(log[1]).toEqual(['capture', divElement]);
1391 - expect(log[2]).toEqual(['bubble', divElement]);
1392 - expect(log[3]).toEqual(['bubble', buttonElement]);
1393 - });
1394 -
1395 - // @gate experimental
1396 - it('handle propagation of click events mixed with onClick events', () => {
1397 - const buttonRef = React.createRef();
1398 - const divRef = React.createRef();
1399 - const log = [];
1400 - const onClick = jest.fn(e => log.push(['bubble', e.currentTarget]));
1401 - const onClickCapture = jest.fn(e =>
1402 - log.push(['capture', e.currentTarget]),
1403 - );
1404 -
1405 - function Test() {
1406 - const click = ReactDOM.unstable_useEvent('click');
1407 - const clickCapture = ReactDOM.unstable_useEvent('click', {
1408 - capture: true,
1409 - });
1410 -
1411 - React.useEffect(() => {
1412 - click.setListener(buttonRef.current, onClick);
1413 - clickCapture.setListener(buttonRef.current, onClickCapture);
1414 - });
1415 -
1416 - return (
1417 - <button ref={buttonRef}>
1418 - <div
1419 - ref={divRef}
1420 - onClick={onClick}
1421 - onClickCapture={onClickCapture}>
1422 - Click me!
1423 - </div>
1424 - </button>
1425 - );
1426 - }
1427 -
1428 - ReactDOM.render(<Test />, container);
1429 - Scheduler.unstable_flushAll();
1430 -
1431 - const buttonElement = buttonRef.current;
1432 - dispatchClickEvent(buttonElement);
1433 - expect(onClick).toHaveBeenCalledTimes(1);
1434 - expect(onClickCapture).toHaveBeenCalledTimes(1);
1435 - expect(log[0]).toEqual(['capture', buttonElement]);
1436 - expect(log[1]).toEqual(['bubble', buttonElement]);
1437 -
1438 - const divElement = divRef.current;
1439 - dispatchClickEvent(divElement);
1440 - expect(onClick).toHaveBeenCalledTimes(3);
1441 - expect(onClickCapture).toHaveBeenCalledTimes(3);
1442 - expect(log[2]).toEqual(['capture', buttonElement]);
1443 - expect(log[3]).toEqual(['capture', divElement]);
1444 - expect(log[4]).toEqual(['bubble', divElement]);
1445 - expect(log[5]).toEqual(['bubble', buttonElement]);
1446 - });
1447 -
1448 - // @gate experimental
1449 - it('should correctly work for a basic "click" listener on the outer target', () => {
1450 - const log = [];
1451 - const clickEvent = jest.fn(event => {
1452 - log.push({
1453 - eventPhase: event.eventPhase,
1454 - type: event.type,
1455 - currentTarget: event.currentTarget,
1456 - target: event.target,
1457 - });
1458 - });
1459 - const divRef = React.createRef();
1460 - const buttonRef = React.createRef();
1461 -
1462 - function Test() {
1463 - const click = ReactDOM.unstable_useEvent('click');
1464 -
1465 - React.useEffect(() => {
1466 - click.setListener(divRef.current, clickEvent);
1467 - });
1468 -
1469 - return (
1470 - <button ref={buttonRef}>
1471 - <div ref={divRef}>Click me!</div>
1472 - </button>
1473 - );
1474 - }
1475 -
1476 - ReactDOM.render(<Test />, container);
1477 - Scheduler.unstable_flushAll();
1478 -
1479 - expect(container.innerHTML).toBe(
1480 - '<button><div>Click me!</div></button>',
1481 - );
1482 -
1483 - // Clicking the button should trigger the event callback
1484 - let divElement = divRef.current;
1485 - dispatchClickEvent(divElement);
1486 - expect(log).toEqual([
1487 - {
1488 - eventPhase: 3,
1489 - type: 'click',
1490 - currentTarget: divRef.current,
1491 - target: divRef.current,
1492 - },
1493 - ]);
1494 -
1495 - // Unmounting the container and clicking should not work
1496 - ReactDOM.render(null, container);
1497 - dispatchClickEvent(divElement);
1498 - expect(clickEvent).toBeCalledTimes(1);
1499 -
1500 - // Re-rendering the container and clicking should work
1501 - ReactDOM.render(<Test />, container);
1502 - Scheduler.unstable_flushAll();
1503 -
1504 - divElement = divRef.current;
1505 - dispatchClickEvent(divElement);
1506 - expect(clickEvent).toBeCalledTimes(2);
1507 -
1508 - // Clicking the button should not work
1509 - const buttonElement = buttonRef.current;
1510 - dispatchClickEvent(buttonElement);
1511 - expect(clickEvent).toBeCalledTimes(2);
1512 - });
1513 -
1514 - // @gate experimental
1515 - it('should correctly handle many nested target listeners', () => {
1516 - const buttonRef = React.createRef();
1517 - const targetListener1 = jest.fn();
1518 - const targetListener2 = jest.fn();
1519 - const targetListener3 = jest.fn();
1520 - const targetListener4 = jest.fn();
1521 -
1522 - function Test() {
1523 - const click1 = ReactDOM.unstable_useEvent('click', {
1524 - capture: true,
1525 - });
1526 - const click2 = ReactDOM.unstable_useEvent('click', {
1527 - capture: true,
1528 - });
1529 - const click3 = ReactDOM.unstable_useEvent('click');
1530 - const click4 = ReactDOM.unstable_useEvent('click');
1531 -
1532 - React.useEffect(() => {
1533 - click1.setListener(buttonRef.current, targetListener1);
1534 - click2.setListener(buttonRef.current, targetListener2);
1535 - click3.setListener(buttonRef.current, targetListener3);
1536 - click4.setListener(buttonRef.current, targetListener4);
1537 - });
1538 -
1539 - return <button ref={buttonRef}>Click me!</button>;
1540 - }
1541 -
1542 - ReactDOM.render(<Test />, container);
1543 - Scheduler.unstable_flushAll();
1544 -
1545 - let buttonElement = buttonRef.current;
1546 - dispatchClickEvent(buttonElement);
1547 -
1548 - expect(targetListener1).toHaveBeenCalledTimes(1);
1549 - expect(targetListener2).toHaveBeenCalledTimes(1);
1550 - expect(targetListener3).toHaveBeenCalledTimes(1);
1551 - expect(targetListener4).toHaveBeenCalledTimes(1);
1552 -
1553 - function Test2() {
1554 - const click1 = ReactDOM.unstable_useEvent('click');
1555 - const click2 = ReactDOM.unstable_useEvent('click');
1556 - const click3 = ReactDOM.unstable_useEvent('click');
1557 - const click4 = ReactDOM.unstable_useEvent('click');
1558 -
1559 - React.useEffect(() => {
1560 - click1.setListener(buttonRef.current, targetListener1);
1561 - click2.setListener(buttonRef.current, targetListener2);
1562 - click3.setListener(buttonRef.current, targetListener3);
1563 - click4.setListener(buttonRef.current, targetListener4);
1564 - });
1565 -
1566 - return <button ref={buttonRef}>Click me!</button>;
1567 - }
1568 -
1569 - ReactDOM.render(<Test2 />, container);
1570 - Scheduler.unstable_flushAll();
1571 -
1572 - buttonElement = buttonRef.current;
1573 - dispatchClickEvent(buttonElement);
1574 - expect(targetListener1).toHaveBeenCalledTimes(2);
1575 - expect(targetListener2).toHaveBeenCalledTimes(2);
1576 - expect(targetListener3).toHaveBeenCalledTimes(2);
1577 - expect(targetListener4).toHaveBeenCalledTimes(2);
1578 - });
1579 -
1580 - // @gate experimental
1581 - it('should correctly handle stopPropagation corrrectly for target events', () => {
1582 - const buttonRef = React.createRef();
1583 - const divRef = React.createRef();
1584 - const clickEvent = jest.fn();
1585 -
1586 - function Test() {
1587 - const click1 = ReactDOM.unstable_useEvent('click', {
1588 - bind: buttonRef,
1589 - });
1590 - const click2 = ReactDOM.unstable_useEvent('click');
1591 -
1592 - React.useEffect(() => {
1593 - click1.setListener(buttonRef.current, clickEvent);
1594 - click2.setListener(divRef.current, e => {
1595 - e.stopPropagation();
1596 - });
1597 - });
1598 -
1599 - return (
1600 - <button ref={buttonRef}>
1601 - <div ref={divRef}>Click me!</div>
1602 - </button>
1603 - );
1604 - }
1605 -
1606 - ReactDOM.render(<Test />, container);
1607 - Scheduler.unstable_flushAll();
1608 -
1609 - const divElement = divRef.current;
1610 - dispatchClickEvent(divElement);
1611 - expect(clickEvent).toHaveBeenCalledTimes(0);
1612 - });
1613 -
1614 - // @gate experimental
1615 - it('should correctly handle stopPropagation corrrectly for many target events', () => {
1616 - const buttonRef = React.createRef();
1617 - const targetListerner1 = jest.fn(e => e.stopPropagation());
1618 - const targetListerner2 = jest.fn(e => e.stopPropagation());
1619 - const targetListerner3 = jest.fn(e => e.stopPropagation());
1620 - const targetListerner4 = jest.fn(e => e.stopPropagation());
1621 -
1622 - function Test() {
1623 - const click1 = ReactDOM.unstable_useEvent('click');
1624 - const click2 = ReactDOM.unstable_useEvent('click');
1625 - const click3 = ReactDOM.unstable_useEvent('click');
1626 - const click4 = ReactDOM.unstable_useEvent('click');
1627 -
1628 - React.useEffect(() => {
1629 - click1.setListener(buttonRef.current, targetListerner1);
1630 - click2.setListener(buttonRef.current, targetListerner2);
1631 - click3.setListener(buttonRef.current, targetListerner3);
1632 - click4.setListener(buttonRef.current, targetListerner4);
1633 - });
1634 -
1635 - return <button ref={buttonRef}>Click me!</button>;
1636 - }
1637 -
1638 - ReactDOM.render(<Test />, container);
1639 - Scheduler.unstable_flushAll();
1640 -
1641 - const buttonElement = buttonRef.current;
1642 - dispatchClickEvent(buttonElement);
1643 - expect(targetListerner1).toHaveBeenCalledTimes(1);
1644 - expect(targetListerner2).toHaveBeenCalledTimes(1);
1645 - expect(targetListerner3).toHaveBeenCalledTimes(1);
1646 - expect(targetListerner4).toHaveBeenCalledTimes(1);
1647 - });
1648 -
1649 - // @gate experimental
1650 - it('should correctly handle stopPropagation for mixed capture/bubbling target listeners', () => {
1651 - const buttonRef = React.createRef();
1652 - const targetListerner1 = jest.fn(e => e.stopPropagation());
1653 - const targetListerner2 = jest.fn(e => e.stopPropagation());
1654 - const targetListerner3 = jest.fn(e => e.stopPropagation());
1655 - const targetListerner4 = jest.fn(e => e.stopPropagation());
1656 -
1657 - function Test() {
1658 - const click1 = ReactDOM.unstable_useEvent('click', {
1659 - capture: true,
1660 - });
1661 - const click2 = ReactDOM.unstable_useEvent('click', {
1662 - capture: true,
1663 - });
1664 - const click3 = ReactDOM.unstable_useEvent('click');
1665 - const click4 = ReactDOM.unstable_useEvent('click');
1666 -
1667 - React.useEffect(() => {
1668 - click1.setListener(buttonRef.current, targetListerner1);
1669 - click2.setListener(buttonRef.current, targetListerner2);
1670 - click3.setListener(buttonRef.current, targetListerner3);
1671 - click4.setListener(buttonRef.current, targetListerner4);
1672 - });
1673 -
1674 - return <button ref={buttonRef}>Click me!</button>;
1675 - }
1676 -
1677 - ReactDOM.render(<Test />, container);
1678 - Scheduler.unstable_flushAll();
1679 -
1680 - const buttonElement = buttonRef.current;
1681 - dispatchClickEvent(buttonElement);
1682 - expect(targetListerner1).toHaveBeenCalledTimes(1);
1683 - expect(targetListerner2).toHaveBeenCalledTimes(1);
1684 - expect(targetListerner3).toHaveBeenCalledTimes(1);
1685 - expect(targetListerner4).toHaveBeenCalledTimes(1);
1686 - });
1687 -
1688 - // @gate experimental
1689 - it('should work with concurrent mode updates', async () => {
1690 - const log = [];
1691 - const ref = React.createRef();
1692 -
1693 - function Test({counter}) {
1694 - const click = ReactDOM.unstable_useEvent('click');
1695 -
1696 - React.useLayoutEffect(() => {
1697 - click.setListener(ref.current, () => {
1698 - log.push({counter});
1699 - });
1700 - });
1701 -
1702 - Scheduler.unstable_yieldValue('Test');
1703 - return <button ref={ref}>Press me</button>;
1704 - }
1705 -
1706 - const root = ReactDOM.createRoot(container);
1707 - root.render(<Test counter={0} />);
1708 -
1709 - expect(Scheduler).toFlushAndYield(['Test']);
1710 -
1711 - // Click the button
1712 - dispatchClickEvent(ref.current);
1713 - expect(log).toEqual([{counter: 0}]);
1714 -
1715 - // Clear log
1716 - log.length = 0;
1717 -
1718 - // Increase counter
1719 - root.render(<Test counter={1} />);
1720 - // Yield before committing
1721 - expect(Scheduler).toFlushAndYieldThrough(['Test']);
1722 -
1723 - // Click the button again
1724 - dispatchClickEvent(ref.current);
1725 - expect(log).toEqual([{counter: 0}]);
1726 -
1727 - // Clear log
1728 - log.length = 0;
1729 -
1730 - // Commit
1731 - expect(Scheduler).toFlushAndYield([]);
1732 - dispatchClickEvent(ref.current);
1733 - expect(log).toEqual([{counter: 1}]);
1734 - });
1735 -
1736 - // @gate experimental
1737 - it('should correctly work for a basic "click" listener that upgrades', () => {
1738 - const clickEvent = jest.fn();
1739 - const buttonRef = React.createRef();
1740 - const button2Ref = React.createRef();
1741 -
1742 - function Test2() {
1743 - const click = ReactDOM.unstable_useEvent('click', {
1744 - passive: false,
1745 - });
1746 -
1747 - React.useEffect(() => {
1748 - click.setListener(button2Ref.current, clickEvent);
1749 - });
1750 -
1751 - return <button ref={button2Ref}>Click me!</button>;
1752 - }
1753 -
1754 - function Test({extra}) {
1755 - const click = ReactDOM.unstable_useEvent('click', {
1756 - passive: true,
1757 - });
1758 -
1759 - React.useEffect(() => {
1760 - click.setListener(buttonRef.current, clickEvent);
1761 - });
1762 -
1763 - return (
1764 - <>
1765 - <button ref={buttonRef}>Click me!</button>
1766 - {extra && <Test2 />}
1767 - </>
1768 - );
1769 - }
1770 -
1771 - ReactDOM.render(<Test />, container);
1772 - Scheduler.unstable_flushAll();
1773 -
1774 - let button = buttonRef.current;
1775 - dispatchClickEvent(button);
1776 - expect(clickEvent).toHaveBeenCalledTimes(1);
1777 -
1778 - ReactDOM.render(<Test extra={true} />, container);
1779 - Scheduler.unstable_flushAll();
1780 -
1781 - clickEvent.mockClear();
1782 -
1783 - button = button2Ref.current;
1784 - dispatchClickEvent(button);
1785 - expect(clickEvent).toHaveBeenCalledTimes(1);
1786 - });
1787 -
1788 - // @gate experimental
1789 - it('should correctly work for a basic "click" listener that upgrades #2', () => {
1790 - const clickEvent = jest.fn();
1791 - const buttonRef = React.createRef();
1792 - const button2Ref = React.createRef();
1793 -
1794 - function Test2() {
1795 - const click = ReactDOM.unstable_useEvent('click', {
1796 - passive: false,
1797 - });
1798 -
1799 - React.useEffect(() => {
1800 - click.setListener(button2Ref.current, clickEvent);
1801 - });
1802 -
1803 - return <button ref={button2Ref}>Click me!</button>;
1804 - }
1805 -
1806 - function Test({extra}) {
1807 - const click = ReactDOM.unstable_useEvent('click', {
1808 - passive: undefined,
1809 - });
1810 -
1811 - React.useEffect(() => {
1812 - click.setListener(buttonRef.current, clickEvent);
1813 - });
1814 -
1815 - return (
1816 - <>
1817 - <button ref={buttonRef}>Click me!</button>
1818 - {extra && <Test2 />}
1819 - </>
1820 - );
1821 - }
1822 -
1823 - ReactDOM.render(<Test />, container);
1824 - Scheduler.unstable_flushAll();
1825 -
1826 - let button = buttonRef.current;
1827 - dispatchClickEvent(button);
1828 - expect(clickEvent).toHaveBeenCalledTimes(1);
1829 -
1830 - ReactDOM.render(<Test extra={true} />, container);
1831 - Scheduler.unstable_flushAll();
1832 -
1833 - clickEvent.mockClear();
1834 -
1835 - button = button2Ref.current;
1836 - dispatchClickEvent(button);
1837 - expect(clickEvent).toHaveBeenCalledTimes(1);
1838 - });
1839 -
1840 - // @gate experimental
1841 - it('should correctly work for a basic "click" window listener', () => {
1842 - const log = [];
1843 - const clickEvent = jest.fn(event => {
1844 - log.push({
1845 - eventPhase: event.eventPhase,
1846 - type: event.type,
1847 - currentTarget: event.currentTarget,
1848 - target: event.target,
1849 - });
1850 - });
1851 -
1852 - function Test() {
1853 - const click = ReactDOM.unstable_useEvent('click');
1854 -
1855 - React.useEffect(() => {
1856 - click.setListener(window, clickEvent);
1857 - });
1858 -
1859 - return <button>Click anything!</button>;
1860 - }
1861 - ReactDOM.render(<Test />, container);
1862 - Scheduler.unstable_flushAll();
1863 -
1864 - expect(container.innerHTML).toBe(
1865 - '<button>Click anything!</button>',
1866 - );
1867 -
1868 - // Clicking outside the button should trigger the event callback
1869 - dispatchClickEvent(document.body);
1870 - expect(log[0]).toEqual({
1871 - eventPhase: 3,
1872 - type: 'click',
1873 - currentTarget: window,
1874 - target: document.body,
1875 - });
1876 -
1877 - // Unmounting the container and clicking should not work
1878 - ReactDOM.render(null, container);
1879 - Scheduler.unstable_flushAll();
1880 -
1881 - dispatchClickEvent(document.body);
1882 - expect(clickEvent).toBeCalledTimes(1);
1883 -
1884 - // Re-rendering and clicking the body should work again
1885 - ReactDOM.render(<Test />, container);
1886 - Scheduler.unstable_flushAll();
1887 -
1888 - dispatchClickEvent(document.body);
1889 - expect(clickEvent).toBeCalledTimes(2);
1890 - });
1891 -
1892 - // @gate experimental
1893 - it('handle propagation of click events on the window', () => {
1894 - const buttonRef = React.createRef();
1895 - const divRef = React.createRef();
1896 - const log = [];
1897 - const onClick = jest.fn(e => log.push(['bubble', e.currentTarget]));
1898 - const onClickCapture = jest.fn(e =>
1899 - log.push(['capture', e.currentTarget]),
1900 - );
1901 -
1902 - function Test() {
1903 - const click = ReactDOM.unstable_useEvent('click');
1904 - const clickCapture = ReactDOM.unstable_useEvent('click', {
1905 - capture: true,
1906 - });
1907 -
1908 - React.useEffect(() => {
1909 - click.setListener(window, onClick);
1910 - clickCapture.setListener(window, onClickCapture);
1911 - click.setListener(buttonRef.current, onClick);
1912 - clickCapture.setListener(buttonRef.current, onClickCapture);
1913 - click.setListener(divRef.current, onClick);
1914 - clickCapture.setListener(divRef.current, onClickCapture);
1915 - });
1916 -
1917 - return (
1918 - <button ref={buttonRef}>
1919 - <div ref={divRef}>Click me!</div>
1920 - </button>
1921 - );
1922 - }
1923 -
1924 - ReactDOM.render(<Test />, container);
1925 - Scheduler.unstable_flushAll();
1926 -
1927 - const buttonElement = buttonRef.current;
1928 - dispatchClickEvent(buttonElement);
1929 - expect(onClick).toHaveBeenCalledTimes(2);
1930 - expect(onClickCapture).toHaveBeenCalledTimes(2);
1931 - expect(log[0]).toEqual(['capture', window]);
1932 - expect(log[1]).toEqual(['capture', buttonElement]);
1933 - expect(log[2]).toEqual(['bubble', buttonElement]);
1934 - expect(log[3]).toEqual(['bubble', window]);
1935 -
1936 - log.length = 0;
1937 - onClick.mockClear();
1938 - onClickCapture.mockClear();
1939 -
1940 - const divElement = divRef.current;
1941 - dispatchClickEvent(divElement);
1942 - expect(onClick).toHaveBeenCalledTimes(3);
1943 - expect(onClickCapture).toHaveBeenCalledTimes(3);
1944 - expect(log[0]).toEqual(['capture', window]);
1945 - expect(log[1]).toEqual(['capture', buttonElement]);
1946 - expect(log[2]).toEqual(['capture', divElement]);
1947 - expect(log[3]).toEqual(['bubble', divElement]);
1948 - expect(log[4]).toEqual(['bubble', buttonElement]);
1949 - expect(log[5]).toEqual(['bubble', window]);
1950 - });
1951 -
1952 - // @gate experimental
1953 - it('should correctly handle stopPropagation for mixed listeners', () => {
1954 - const buttonRef = React.createRef();
1955 - const rootListerner1 = jest.fn(e => e.stopPropagation());
1956 - const rootListerner2 = jest.fn();
1957 - const targetListerner1 = jest.fn();
1958 - const targetListerner2 = jest.fn();
1959 -
1960 - function Test() {
1961 - const click1 = ReactDOM.unstable_useEvent('click', {
1962 - capture: true,
1963 - });
1964 - const click2 = ReactDOM.unstable_useEvent('click', {
1965 - capture: true,
1966 - });
1967 - const click3 = ReactDOM.unstable_useEvent('click');
1968 - const click4 = ReactDOM.unstable_useEvent('click');
1969 -
1970 - React.useEffect(() => {
1971 - click1.setListener(window, rootListerner1);
1972 - click2.setListener(buttonRef.current, targetListerner1);
1973 - click3.setListener(window, rootListerner2);
1974 - click4.setListener(buttonRef.current, targetListerner2);
1975 - });
1976 -
1977 - return <button ref={buttonRef}>Click me!</button>;
1978 - }
1979 -
1980 - ReactDOM.render(<Test />, container);
1981 - Scheduler.unstable_flushAll();
1982 -
1983 - const buttonElement = buttonRef.current;
1984 - dispatchClickEvent(buttonElement);
1985 - expect(rootListerner1).toHaveBeenCalledTimes(1);
1986 - expect(targetListerner1).toHaveBeenCalledTimes(0);
1987 - expect(targetListerner2).toHaveBeenCalledTimes(0);
1988 - expect(rootListerner2).toHaveBeenCalledTimes(0);
1989 - });
1990 -
1991 - // @gate experimental
1992 - it('should correctly handle stopPropagation for delegated listeners', () => {
1993 - const buttonRef = React.createRef();
1994 - const rootListerner1 = jest.fn(e => e.stopPropagation());
1995 - const rootListerner2 = jest.fn();
1996 - const rootListerner3 = jest.fn(e => e.stopPropagation());
1997 - const rootListerner4 = jest.fn();
1998 -
1999 - function Test() {
2000 - const click1 = ReactDOM.unstable_useEvent('click', {
2001 - capture: true,
2002 - });
2003 - const click2 = ReactDOM.unstable_useEvent('click', {
2004 - capture: true,
2005 - });
2006 - const click3 = ReactDOM.unstable_useEvent('click');
2007 - const click4 = ReactDOM.unstable_useEvent('click');
2008 -
2009 - React.useEffect(() => {
2010 - click1.setListener(window, rootListerner1);
2011 - click2.setListener(window, rootListerner2);
2012 - click3.setListener(window, rootListerner3);
2013 - click4.setListener(window, rootListerner4);
2014 - });
2015 -
2016 - return <button ref={buttonRef}>Click me!</button>;
2017 - }
2018 -
2019 - ReactDOM.render(<Test />, container);
2020 -
2021 - Scheduler.unstable_flushAll();
2022 -
2023 - const buttonElement = buttonRef.current;
2024 - dispatchClickEvent(buttonElement);
2025 - expect(rootListerner1).toHaveBeenCalledTimes(1);
2026 - expect(rootListerner2).toHaveBeenCalledTimes(1);
2027 - expect(rootListerner3).toHaveBeenCalledTimes(0);
2028 - expect(rootListerner4).toHaveBeenCalledTimes(0);
2029 - });
2030 -
2031 - // @gate experimental
2032 - it('handle propagation of click events on the window and document', () => {
2033 - const buttonRef = React.createRef();
2034 - const divRef = React.createRef();
2035 - const log = [];
2036 - const onClick = jest.fn(e => log.push(['bubble', e.currentTarget]));
2037 - const onClickCapture = jest.fn(e =>
2038 - log.push(['capture', e.currentTarget]),
2039 - );
2040 -
2041 - function Test() {
2042 - const click = ReactDOM.unstable_useEvent('click');
2043 - const clickCapture = ReactDOM.unstable_useEvent('click', {
2044 - capture: true,
2045 - });
2046 -
2047 - React.useEffect(() => {
2048 - click.setListener(window, onClick);
2049 - clickCapture.setListener(window, onClickCapture);
2050 - click.setListener(document, onClick);
2051 - clickCapture.setListener(document, onClickCapture);
2052 - click.setListener(buttonRef.current, onClick);
2053 - clickCapture.setListener(buttonRef.current, onClickCapture);
2054 - click.setListener(divRef.current, onClick);
2055 - clickCapture.setListener(divRef.current, onClickCapture);
2056 - });
2057 -
2058 - return (
2059 - <button ref={buttonRef}>
2060 - <div ref={divRef}>Click me!</div>
2061 - </button>
2062 - );
2063 - }
2064 -
2065 - ReactDOM.render(<Test />, container);
2066 - Scheduler.unstable_flushAll();
2067 -
2068 - const buttonElement = buttonRef.current;
2069 - dispatchClickEvent(buttonElement);
2070 - expect(onClick).toHaveBeenCalledTimes(3);
2071 - expect(onClickCapture).toHaveBeenCalledTimes(3);
2072 - expect(log[0]).toEqual(['capture', window]);
2073 - expect(log[1]).toEqual(['capture', document]);
2074 - expect(log[2]).toEqual(['capture', buttonElement]);
2075 - expect(log[3]).toEqual(['bubble', buttonElement]);
2076 - expect(log[4]).toEqual(['bubble', document]);
2077 - expect(log[5]).toEqual(['bubble', window]);
2078 -
2079 - log.length = 0;
2080 - onClick.mockClear();
2081 - onClickCapture.mockClear();
2082 -
2083 - const divElement = divRef.current;
2084 - dispatchClickEvent(divElement);
2085 - expect(onClick).toHaveBeenCalledTimes(4);
2086 - expect(onClickCapture).toHaveBeenCalledTimes(4);
2087 - expect(log[0]).toEqual(['capture', window]);
2088 - expect(log[1]).toEqual(['capture', document]);
2089 - expect(log[2]).toEqual(['capture', buttonElement]);
2090 - expect(log[3]).toEqual(['capture', divElement]);
2091 - expect(log[4]).toEqual(['bubble', divElement]);
2092 - expect(log[5]).toEqual(['bubble', buttonElement]);
2093 - expect(log[6]).toEqual(['bubble', document]);
2094 - expect(log[7]).toEqual(['bubble', window]);
2095 - });
2096 -
2097 - // @gate experimental
2098 - it('handles propagation of custom user events', () => {
2099 - const buttonRef = React.createRef();
2100 - const divRef = React.createRef();
2101 - const log = [];
2102 - const onCustomEvent = jest.fn(e =>
2103 - log.push(['bubble', e.currentTarget]),
2104 - );
2105 - const onCustomEventCapture = jest.fn(e =>
2106 - log.push(['capture', e.currentTarget]),
2107 - );
2108 -
2109 - function Test() {
2110 - let customEventHandle;
2111 -
2112 - // Test that we get a warning when we don't provide an explicit priortiy
2113 - expect(() => {
2114 - customEventHandle = ReactDOM.unstable_useEvent('custom-event');
2115 - }).toWarnDev(
2116 - 'Warning: The event "type" provided to useEvent() does not have a known priority type. ' +
2117 - 'It is recommended to provide a "priority" option to specify a priority.',
2118 - );
2119 -
2120 - customEventHandle = ReactDOM.unstable_useEvent('custom-event', {
2121 - priority: 0, // Discrete
2122 - });
2123 -
2124 - const customCaptureHandle = ReactDOM.unstable_useEvent(
2125 - 'custom-event',
2126 - {
2127 - capture: true,
2128 - priority: 0, // Discrete
2129 - },
2130 - );
2131 -
2132 - React.useEffect(() => {
2133 - customEventHandle.setListener(buttonRef.current, onCustomEvent);
2134 - customCaptureHandle.setListener(
2135 - buttonRef.current,
2136 - onCustomEventCapture,
2137 - );
2138 - customEventHandle.setListener(divRef.current, onCustomEvent);
2139 - customCaptureHandle.setListener(
2140 - divRef.current,
2141 - onCustomEventCapture,
2142 - );
2143 - });
2144 -
2145 - return (
2146 - <button ref={buttonRef}>
2147 - <div ref={divRef}>Click me!</div>
2148 - </button>
2149 - );
2150 - }
2151 -
2152 - ReactDOM.render(<Test />, container);
2153 - Scheduler.unstable_flushAll();
2154 -
2155 - const buttonElement = buttonRef.current;
2156 - dispatchEvent(buttonElement, 'custom-event');
2157 - expect(onCustomEvent).toHaveBeenCalledTimes(1);
2158 - expect(onCustomEventCapture).toHaveBeenCalledTimes(1);
2159 - expect(log[0]).toEqual(['capture', buttonElement]);
2160 - expect(log[1]).toEqual(['bubble', buttonElement]);
2161 -
2162 - const divElement = divRef.current;
2163 - dispatchEvent(divElement, 'custom-event');
2164 - expect(onCustomEvent).toHaveBeenCalledTimes(3);
2165 - expect(onCustomEventCapture).toHaveBeenCalledTimes(3);
2166 - expect(log[2]).toEqual(['capture', buttonElement]);
2167 - expect(log[3]).toEqual(['capture', divElement]);
2168 - expect(log[4]).toEqual(['bubble', divElement]);
2169 - expect(log[5]).toEqual(['bubble', buttonElement]);
2170 - });
2171 -
2172 - // @gate experimental
2173 - it('beforeblur and afterblur are called after a focused element is unmounted', () => {
2174 - const log = [];
2175 - // We have to persist here because we want to read relatedTarget later.
2176 - const onAfterBlur = jest.fn(e => {
2177 - e.persist();
2178 - log.push(e.type);
2179 - });
2180 - const onBeforeBlur = jest.fn(e => log.push(e.type));
2181 - const innerRef = React.createRef();
2182 - const innerRef2 = React.createRef();
2183 -
2184 - const Component = ({show}) => {
2185 - const ref = React.useRef(null);
2186 - const afterBlurHandle = ReactDOM.unstable_useEvent('afterblur');
2187 - const beforeBlurHandle = ReactDOM.unstable_useEvent('beforeblur');
2188 -
2189 - React.useEffect(() => {
2190 - afterBlurHandle.setListener(document, onAfterBlur);
2191 - beforeBlurHandle.setListener(ref.current, onBeforeBlur);
2192 - });
2193 -
2194 - return (
2195 - <div ref={ref}>
2196 - {show && <input ref={innerRef} />}
2197 - <div ref={innerRef2} />
2198 - </div>
2199 - );
2200 - };
2201 -
2202 - ReactDOM.render(<Component show={true} />, container);
2203 - Scheduler.unstable_flushAll();
2204 -
2205 - const inner = innerRef.current;
2206 - const target = createEventTarget(inner);
2207 - target.focus();
2208 - expect(onBeforeBlur).toHaveBeenCalledTimes(0);
2209 - expect(onAfterBlur).toHaveBeenCalledTimes(0);
2210 -
2211 - ReactDOM.render(<Component show={false} />, container);
2212 - Scheduler.unstable_flushAll();
2213 -
2214 - expect(onBeforeBlur).toHaveBeenCalledTimes(1);
2215 - expect(onAfterBlur).toHaveBeenCalledTimes(1);
2216 - expect(onAfterBlur).toHaveBeenCalledWith(
2217 - expect.objectContaining({relatedTarget: inner}),
2218 - );
2219 - expect(log).toEqual(['beforeblur', 'afterblur']);
2220 - });
2221 -
2222 - // @gate experimental
2223 - it('beforeblur and afterblur are called after a nested focused element is unmounted', () => {
2224 - const log = [];
2225 - // We have to persist here because we want to read relatedTarget later.
2226 - const onAfterBlur = jest.fn(e => {
2227 - e.persist();
2228 - log.push(e.type);
2229 - });
2230 - const onBeforeBlur = jest.fn(e => log.push(e.type));
2231 - const innerRef = React.createRef();
2232 - const innerRef2 = React.createRef();
2233 -
2234 - const Component = ({show}) => {
2235 - const ref = React.useRef(null);
2236 - const afterBlurHandle = ReactDOM.unstable_useEvent('afterblur');
2237 - const beforeBlurHandle = ReactDOM.unstable_useEvent('beforeblur');
2238 -
2239 - React.useEffect(() => {
2240 - afterBlurHandle.setListener(document, onAfterBlur);
2241 - beforeBlurHandle.setListener(ref.current, onBeforeBlur);
2242 - });
2243 -
2244 - return (
2245 - <div ref={ref}>
2246 - {show && (
2247 - <div>
2248 - <input ref={innerRef} />
2249 - </div>
2250 - )}
2251 - <div ref={innerRef2} />
2252 - </div>
2253 - );
2254 - };
2255 -
2256 - ReactDOM.render(<Component show={true} />, container);
2257 - Scheduler.unstable_flushAll();
2258 -
2259 - const inner = innerRef.current;
2260 - const target = createEventTarget(inner);
2261 - target.focus();
2262 - expect(onBeforeBlur).toHaveBeenCalledTimes(0);
2263 - expect(onAfterBlur).toHaveBeenCalledTimes(0);
2264 -
2265 - ReactDOM.render(<Component show={false} />, container);
2266 - Scheduler.unstable_flushAll();
2267 -
2268 - expect(onBeforeBlur).toHaveBeenCalledTimes(1);
2269 - expect(onAfterBlur).toHaveBeenCalledTimes(1);
2270 - expect(onAfterBlur).toHaveBeenCalledWith(
2271 - expect.objectContaining({relatedTarget: inner}),
2272 - );
2273 - expect(log).toEqual(['beforeblur', 'afterblur']);
2274 - });
2275 -
2276 - // @gate experimental
2277 - it('beforeblur and afterblur are called after a focused element is suspended', () => {
2278 - const log = [];
2279 - // We have to persist here because we want to read relatedTarget later.
2280 - const onAfterBlur = jest.fn(e => {
2281 - e.persist();
2282 - log.push(e.type);
2283 - });
2284 - const onBeforeBlur = jest.fn(e => log.push(e.type));
2285 - const innerRef = React.createRef();
2286 - const Suspense = React.Suspense;
2287 - let suspend = false;
2288 - let resolve;
2289 - const promise = new Promise(
2290 - resolvePromise => (resolve = resolvePromise),
2291 - );
2292 -
2293 - function Child() {
2294 - if (suspend) {
2295 - throw promise;
2296 - } else {
2297 - return <input ref={innerRef} />;
2298 - }
2299 - }
2300 -
2301 - const Component = () => {
2302 - const ref = React.useRef(null);
2303 - const afterBlurHandle = ReactDOM.unstable_useEvent('afterblur');
2304 - const beforeBlurHandle = ReactDOM.unstable_useEvent('beforeblur');
2305 -
2306 - React.useEffect(() => {
2307 - afterBlurHandle.setListener(document, onAfterBlur);
2308 - beforeBlurHandle.setListener(ref.current, onBeforeBlur);
2309 - });
2310 -
2311 - return (
2312 - <div ref={ref}>
2313 - <Suspense fallback="Loading...">
2314 - <Child />
2315 - </Suspense>
2316 - </div>
2317 - );
2318 - };
2319 -
2320 - const container2 = document.createElement('div');
2321 - document.body.appendChild(container2);
2322 -
2323 - const root = ReactDOM.createRoot(container2);
2324 -
2325 - ReactTestUtils.act(() => {
2326 - root.render(<Component />);
2327 - });
2328 - jest.runAllTimers();
2329 -
2330 - const inner = innerRef.current;
2331 - const target = createEventTarget(inner);
2332 - target.focus();
2333 - expect(onBeforeBlur).toHaveBeenCalledTimes(0);
2334 - expect(onAfterBlur).toHaveBeenCalledTimes(0);
2335 -
2336 - suspend = true;
2337 - ReactTestUtils.act(() => {
2338 - root.render(<Component />);
2339 - });
2340 - jest.runAllTimers();
2341 -
2342 - expect(onBeforeBlur).toHaveBeenCalledTimes(1);
2343 - expect(onAfterBlur).toHaveBeenCalledTimes(1);
2344 - expect(onAfterBlur).toHaveBeenCalledWith(
2345 - expect.objectContaining({relatedTarget: inner}),
2346 - );
2347 - resolve();
2348 - expect(log).toEqual(['beforeblur', 'afterblur']);
2349 -
2350 - document.body.removeChild(container2);
2351 - });
2352 -
2353 - describe('Compatibility with Scopes API', () => {
2354 - beforeEach(() => {
2355 - jest.resetModules();
2356 - ReactFeatureFlags = require('shared/ReactFeatureFlags');
2357 - ReactFeatureFlags.enableModernEventSystem = true;
2358 - ReactFeatureFlags.enableUseEventAPI = true;
2359 - ReactFeatureFlags.enableScopeAPI = true;
2360 -
2361 - React = require('react');
2362 - ReactDOM = require('react-dom');
2363 - Scheduler = require('scheduler');
2364 - ReactDOMServer = require('react-dom/server');
2365 - });
2366 -
2367 - // @gate experimental
2368 - it('handle propagation of click events on a scope', () => {
2369 - const buttonRef = React.createRef();
2370 - const log = [];
2371 - const onClick = jest.fn(e =>
2372 - log.push(['bubble', e.currentTarget]),
2373 - );
2374 - const onClickCapture = jest.fn(e =>
2375 - log.push(['capture', e.currentTarget]),
2376 - );
2377 - const TestScope = React.unstable_createScope();
2378 -
2379 - function Test() {
2380 - const click = ReactDOM.unstable_useEvent('click');
2381 - const clickCapture = ReactDOM.unstable_useEvent('click', {
2382 - capture: true,
2383 - });
2384 - const scopeRef = React.useRef(null);
2385 -
2386 - React.useEffect(() => {
2387 - click.setListener(scopeRef.current, onClick);
2388 - clickCapture.setListener(scopeRef.current, onClickCapture);
2389 - });
2390 -
2391 - return (
2392 - <TestScope ref={scopeRef}>
2393 - <button ref={buttonRef} />
2394 - </TestScope>
2395 - );
2396 - }
2397 -
2398 - ReactDOM.render(<Test />, container);
2399 - Scheduler.unstable_flushAll();
2400 -
2401 - const buttonElement = buttonRef.current;
2402 - dispatchClickEvent(buttonElement);
2403 -
2404 - expect(onClick).toHaveBeenCalledTimes(1);
2405 - expect(onClickCapture).toHaveBeenCalledTimes(1);
2406 - expect(log).toEqual([
2407 - ['capture', buttonElement],
2408 - ['bubble', buttonElement],
2409 - ]);
2410 - });
2411 -
2412 - // @gate experimental
2413 - it('handle mixed propagation of click events on a scope', () => {
2414 - const buttonRef = React.createRef();
2415 - const divRef = React.createRef();
2416 - const log = [];
2417 - const onClick = jest.fn(e =>
2418 - log.push(['bubble', e.currentTarget]),
2419 - );
2420 - const onClickCapture = jest.fn(e =>
2421 - log.push(['capture', e.currentTarget]),
2422 - );
2423 - const TestScope = React.unstable_createScope();
2424 -
2425 - function Test() {
2426 - const click = ReactDOM.unstable_useEvent('click');
2427 - const clickCapture = ReactDOM.unstable_useEvent('click', {
2428 - capture: true,
2429 - });
2430 - const scopeRef = React.useRef(null);
2431 -
2432 - React.useEffect(() => {
2433 - click.setListener(scopeRef.current, onClick);
2434 - clickCapture.setListener(scopeRef.current, onClickCapture);
2435 - click.setListener(buttonRef.current, onClick);
2436 - clickCapture.setListener(buttonRef.current, onClickCapture);
2437 - });
2438 -
2439 - return (
2440 - <TestScope ref={scopeRef}>
2441 - <button ref={buttonRef}>
2442 - <div
2443 - ref={divRef}
2444 - onClick={onClick}
2445 - onClickCapture={onClickCapture}>
2446 - Click me!
2447 - </div>
2448 - </button>
2449 - </TestScope>
2450 - );
2451 - }
2452 -
2453 - ReactDOM.render(<Test />, container);
2454 - Scheduler.unstable_flushAll();
2455 -
2456 - const buttonElement = buttonRef.current;
2457 - dispatchClickEvent(buttonElement);
2458 -
2459 - expect(onClick).toHaveBeenCalledTimes(2);
2460 - expect(onClickCapture).toHaveBeenCalledTimes(2);
2461 - expect(log).toEqual([
2462 - ['capture', buttonElement],
2463 - ['capture', buttonElement],
2464 - ['bubble', buttonElement],
2465 - ['bubble', buttonElement],
2466 - ]);
2467 -
2468 - log.length = 0;
2469 - onClick.mockClear();
2470 - onClickCapture.mockClear();
2471 -
2472 - const divElement = divRef.current;
2473 - dispatchClickEvent(divElement);
2474 -
2475 - expect(onClick).toHaveBeenCalledTimes(3);
2476 - expect(onClickCapture).toHaveBeenCalledTimes(3);
2477 - expect(log).toEqual([
2478 - ['capture', buttonElement],
2479 - ['capture', buttonElement],
2480 - ['capture', divElement],
2481 - ['bubble', divElement],
2482 - ['bubble', buttonElement],
2483 - ['bubble', buttonElement],
2484 - ]);
2485 - });
2486 -
2487 - // @gate experimental
2488 - it('should not handle the target being a dangling text node within a scope', () => {
2489 - const clickEvent = jest.fn();
2490 - const buttonRef = React.createRef();
2491 -
2492 - const TestScope = React.unstable_createScope();
2493 -
2494 - function Test() {
2495 - const click = ReactDOM.unstable_useEvent('click');
2496 - const scopeRef = React.useRef(null);
2497 -
2498 - React.useEffect(() => {
2499 - click.setListener(scopeRef.current, clickEvent);
2500 - });
2501 -
2502 - return (
2503 - <button ref={buttonRef}>
2504 - <TestScope ref={scopeRef}>Click me!</TestScope>
2505 - </button>
2506 - );
2507 - }
2508 -
2509 - ReactDOM.render(<Test />, container);
2510 - Scheduler.unstable_flushAll();
2511 -
2512 - const textNode = buttonRef.current.firstChild;
2513 - dispatchClickEvent(textNode);
2514 - // This should not work, as the target instance will be the
2515 - // <button>, which is actually outside the scope.
2516 - expect(clickEvent).toBeCalledTimes(0);
2517 - });
2518 -
2519 - // @gate experimental
2520 - it('handle stopPropagation (inner) correctly between scopes', () => {
2521 - const buttonRef = React.createRef();
2522 - const outerOnClick = jest.fn();
2523 - const innerOnClick = jest.fn(e => e.stopPropagation());
2524 - const TestScope = React.unstable_createScope();
2525 - const TestScope2 = React.unstable_createScope();
2526 -
2527 - function Test() {
2528 - const click = ReactDOM.unstable_useEvent('click');
2529 - const scopeRef = React.useRef(null);
2530 - const scope2Ref = React.useRef(null);
2531 -
2532 - React.useEffect(() => {
2533 - click.setListener(scopeRef.current, outerOnClick);
2534 - click.setListener(scope2Ref.current, innerOnClick);
2535 - });
2536 -
2537 - return (
2538 - <TestScope ref={scopeRef}>
2539 - <TestScope2 ref={scope2Ref}>
2540 - <button ref={buttonRef} />
2541 - </TestScope2>
2542 - </TestScope>
2543 - );
2544 - }
2545 -
2546 - ReactDOM.render(<Test />, container);
2547 - Scheduler.unstable_flushAll();
2548 -
2549 - const buttonElement = buttonRef.current;
2550 - dispatchClickEvent(buttonElement);
2551 -
2552 - expect(innerOnClick).toHaveBeenCalledTimes(1);
2553 - expect(outerOnClick).toHaveBeenCalledTimes(0);
2554 - });
2555 -
2556 - // @gate experimental
2557 - it('handle stopPropagation (outer) correctly between scopes', () => {
2558 - const buttonRef = React.createRef();
2559 - const outerOnClick = jest.fn(e => e.stopPropagation());
2560 - const innerOnClick = jest.fn();
2561 - const TestScope = React.unstable_createScope();
2562 - const TestScope2 = React.unstable_createScope();
2563 -
2564 - function Test() {
2565 - const click = ReactDOM.unstable_useEvent('click');
2566 - const scopeRef = React.useRef(null);
2567 - const scope2Ref = React.useRef(null);
2568 -
2569 - React.useEffect(() => {
2570 - click.setListener(scopeRef.current, outerOnClick);
2571 - click.setListener(scope2Ref.current, innerOnClick);
2572 - });
2573 -
2574 - return (
2575 - <TestScope ref={scopeRef}>
2576 - <TestScope2 ref={scope2Ref}>
2577 - <button ref={buttonRef} />
2578 - </TestScope2>
2579 - </TestScope>
2580 - );
2581 - }
2582 -
2583 - ReactDOM.render(<Test />, container);
2584 - Scheduler.unstable_flushAll();
2585 -
2586 - const buttonElement = buttonRef.current;
2587 - dispatchClickEvent(buttonElement);
2588 -
2589 - expect(innerOnClick).toHaveBeenCalledTimes(1);
2590 - expect(outerOnClick).toHaveBeenCalledTimes(1);
2591 - });
2592 -
2593 - // @gate experimental
2594 -
2595 - it('handle stopPropagation (inner and outer) correctly between scopes', () => {
2596 - const buttonRef = React.createRef();
2597 - const onClick = jest.fn(e => e.stopPropagation());
2598 - const TestScope = React.unstable_createScope();
2599 - const TestScope2 = React.unstable_createScope();
2600 -
2601 - function Test() {
2602 - const click = ReactDOM.unstable_useEvent('click');
2603 - const scopeRef = React.useRef(null);
2604 - const scope2Ref = React.useRef(null);
2605 -
2606 - React.useEffect(() => {
2607 - click.setListener(scopeRef.current, onClick);
2608 - click.setListener(scope2Ref.current, onClick);
2609 - });
2610 -
2611 - return (
2612 - <TestScope ref={scopeRef}>
2613 - <TestScope2 ref={scope2Ref}>
2614 - <button ref={buttonRef} />
2615 - </TestScope2>
2616 - </TestScope>
2617 - );
2618 - }
2619 -
2620 - ReactDOM.render(<Test />, container);
2621 - Scheduler.unstable_flushAll();
2622 -
2623 - const buttonElement = buttonRef.current;
2624 - dispatchClickEvent(buttonElement);
2625 -
2626 - expect(onClick).toHaveBeenCalledTimes(1);
2627 - });
2628 - });
2629 - });
1081 },
1082 );
1083 }
packages/react-dom/src/events/plugins/ModernSimpleEventPlugin.js
+2 -23
@@ -17,17 +17,13 @@ import type {PluginModule} from 'legacy-events/PluginModuleType';
17 import type {EventSystemFlags} from '../EventSystemFlags';
18
19 import SyntheticEvent from 'legacy-events/SyntheticEvent';
20 -import {IS_TARGET_PHASE_ONLY} from '../EventSystemFlags';
20
21 import * as DOMTopLevelEventTypes from '../DOMTopLevelEventTypes';
22 import {
23 topLevelEventsToDispatchConfig,
24 simpleEventPluginEventTypes,
25 } from '../DOMEventProperties';
27 -import {
28 - accumulateEventTargetListeners,
29 - accumulateTwoPhaseListeners,
30 -} from '../DOMModernPluginEventSystem';
26 +import {accumulateTwoPhaseListeners} from '../DOMModernPluginEventSystem';
27
28 import SyntheticAnimationEvent from '../SyntheticAnimationEvent';
29 import SyntheticClipboardEvent from '../SyntheticClipboardEvent';
@@ -42,8 +38,6 @@ import SyntheticUIEvent from '../SyntheticUIEvent';
38 import SyntheticWheelEvent from '../SyntheticWheelEvent';
39 import getEventCharCode from '../getEventCharCode';
40
45 -import {enableUseEventAPI} from 'shared/ReactFeatureFlags';
46 -
41 // Only used in DEV for exhaustiveness validation.
42 const knownHTMLTopLevelTypes: Array<DOMTopLevelEventType> = [
43 DOMTopLevelEventTypes.TOP_ABORT,
@@ -204,22 +198,7 @@ const SimpleEventPlugin: PluginModule<MouseEvent> = {
198 nativeEventTarget,
199 );
200
207 - // For TargetEvent only accumulation, we do not traverse through
208 - // the React tree looking for managed React DOM elements that have
209 - // events. Instead we only check the EventTarget Store Map to see
210 - // if the container has listeners for the particular phase we're
211 - // interested in. This is because we attach the native event listener
212 - // only in the given phase.
213 - if (
214 - enableUseEventAPI &&
215 - eventSystemFlags !== undefined &&
216 - eventSystemFlags & IS_TARGET_PHASE_ONLY &&
217 - targetContainer != null
218 - ) {
219 - accumulateEventTargetListeners(event, targetContainer);
220 - } else {
221 - accumulateTwoPhaseListeners(event, true);
222 - }
201 + accumulateTwoPhaseListeners(event);
202 return event;
203 },
204 };
packages/react-dom/src/server/ReactPartialRendererHooks.js
-9
@@ -19,7 +19,6 @@ import type {
19 ReactEventResponderListener,
20 } from 'shared/ReactTypes';
21 import type {SuspenseConfig} from 'react-reconciler/src/ReactFiberSuspenseConfig';
22 -import type {ReactDOMListenerMap} from '../shared/ReactDOMTypes';
22
23 import {validateContextBounds} from './ReactPartialRendererContext';
24 import {makeServerId} from '../client/ReactDOMHostConfig';
@@ -499,13 +498,6 @@ function useOpaqueIdentifier(): OpaqueIDType {
498 return makeServerId();
499 }
500
502 -function useEvent(event: any): ReactDOMListenerMap {
503 - return {
504 - clear: noop,
505 - setListener: noop,
506 - };
507 -}
508 -
501 function noop(): void {}
502
503 export let currentThreadID: ThreadID = 0;
@@ -532,7 +524,6 @@ export const Dispatcher: DispatcherType = {
524 useResponder,
525 useDeferredValue,
526 useTransition,
535 - useEvent,
527 useOpaqueIdentifier,
528 // Subscriptions are not setup in a server environment.
529 useMutableSource,
packages/react-dom/src/shared/ReactDOMTypes.js
-24
@@ -12,9 +12,7 @@ import type {
12 ReactEventResponder,
13 ReactEventResponderInstance,
14 EventPriority,
15 - ReactScopeMethods,
15 } from 'shared/ReactTypes';
17 -import type {DOMTopLevelEventType} from 'legacy-events/TopLevelEventTypes';
16
17 type AnyNativeEvent = Event | KeyboardEvent | MouseEvent | Touch;
18
@@ -77,25 +75,3 @@ export type ReactDOMResponderContext = {
75 getResponderNode(): Element | null,
76 ...
77 };
80 -
81 -export type ReactDOMListenerEvent = {|
82 - capture: boolean,
83 - passive: void | boolean,
84 - priority: EventPriority,
85 - type: DOMTopLevelEventType,
86 -|};
87 -
88 -export type ReactDOMListenerMap = {|
89 - clear: () => void,
90 - setListener: (
91 - target: EventTarget | ReactScopeMethods,
92 - callback: ?(SyntheticEvent<EventTarget>) => void,
93 - ) => void,
94 -|};
95 -
96 -export type ReactDOMListener = {|
97 - callback: (SyntheticEvent<EventTarget>) => void,
98 - destroy: Node => void,
99 - event: ReactDOMListenerEvent,
100 - target: EventTarget | ReactScopeMethods,
101 -|};
packages/react-interactions/events/focus.js deleted
-10
@@ -1,10 +0,0 @@
1 -/**
2 - * Copyright (c) Facebook, Inc. and its affiliates.
3 - *
4 - * This source code is licensed under the MIT license found in the
5 - * LICENSE file in the root directory of this source tree.
6 - *
7 - * @flow
8 - */
9 -
10 -export * from './src/dom/use-event/Focus';
packages/react-interactions/events/src/dom/use-event/Focus.js deleted
-440
@@ -1,440 +0,0 @@
1 -/**
2 - * Copyright (c) Facebook, Inc. and its affiliates.
3 - *
4 - * This source code is licensed under the MIT license found in the
5 - * LICENSE file in the root directory of this source tree.
6 - *
7 - * @flow
8 - */
9 -
10 -import * as React from 'react';
11 -import * as ReactDOM from 'react-dom';
12 -
13 -const useEvent = ReactDOM.unstable_useEvent;
14 -const {useEffect, useRef} = React;
15 -
16 -type UseFocusOptions = {|
17 - disabled?: boolean,
18 - onBlur?: (SyntheticEvent<EventTarget>) => void,
19 - onFocus?: (SyntheticEvent<EventTarget>) => void,
20 - onFocusChange?: boolean => void,
21 - onFocusVisibleChange?: boolean => void,
22 -|};
23 -
24 -type UseFocusWithinOptions = {|
25 - disabled?: boolean,
26 - onAfterBlurWithin?: (SyntheticEvent<EventTarget>) => void,
27 - onBeforeBlurWithin?: (SyntheticEvent<EventTarget>) => void,
28 - onBlurWithin?: (SyntheticEvent<EventTarget>) => void,
29 - onFocusWithin?: (SyntheticEvent<EventTarget>) => void,
30 - onFocusWithinChange?: boolean => void,
31 - onFocusWithinVisibleChange?: boolean => void,
32 -|};
33 -
34 -const isMac =
35 - typeof window !== 'undefined' && window.navigator != null
36 - ? /^Mac/.test(window.navigator.platform)
37 - : false;
38 -
39 -const canUseDOM: boolean = !!(
40 - typeof window !== 'undefined' &&
41 - typeof window.document !== 'undefined' &&
42 - typeof window.document.createElement !== 'undefined'
43 -);
44 -
45 -let passiveBrowserEventsSupported = false;
46 -
47 -// Check if browser support events with passive listeners
48 -// https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support
49 -if (canUseDOM) {
50 - try {
51 - const options = {};
52 - // $FlowFixMe: Ignore Flow complaining about needing a value
53 - Object.defineProperty(options, 'passive', {
54 - get: function() {
55 - passiveBrowserEventsSupported = true;
56 - },
57 - });
58 - window.addEventListener('test', options, options);
59 - window.removeEventListener('test', options, options);
60 - } catch (e) {
61 - passiveBrowserEventsSupported = false;
62 - }
63 -}
64 -
65 -const hasPointerEvents =
66 - typeof window !== 'undefined' && window.PointerEvent != null;
67 -
68 -const globalFocusVisibleEvents = hasPointerEvents
69 - ? ['keydown', 'pointermove', 'pointerdown', 'pointerup']
70 - : ['keydown', 'mousedown', 'touchmove', 'touchstart', 'touchend'];
71 -
72 -// Global state for tracking focus visible and emulation of mouse
73 -let isGlobalFocusVisible = true;
74 -let hasTrackedGlobalFocusVisible = false;
75 -let isEmulatingMouseEvents = false;
76 -
77 -function trackGlobalFocusVisible() {
78 - globalFocusVisibleEvents.forEach(type => {
79 - window.addEventListener(
80 - type,
81 - handleGlobalFocusVisibleEvent,
82 - passiveBrowserEventsSupported ? {capture: true, passive: true} : true,
83 - );
84 - });
85 -}
86 -
87 -function handleGlobalFocusVisibleEvent(
88 - nativeEvent: MouseEvent | TouchEvent | KeyboardEvent,
89 -): void {
90 - const {type} = nativeEvent;
91 -
92 - switch (type) {
93 - case 'pointermove':
94 - case 'pointerdown':
95 - case 'pointerup': {
96 - isGlobalFocusVisible = false;
97 - break;
98 - }
99 -
100 - case 'keydown': {
101 - const {metaKey, altKey, ctrlKey} = nativeEvent;
102 - const validKey = !(metaKey || (!isMac && altKey) || ctrlKey);
103 -
104 - if (validKey) {
105 - isGlobalFocusVisible = true;
106 - }
107 - break;
108 - }
109 -
110 - // fallbacks for no PointerEvent support
111 - case 'touchmove':
112 - case 'touchstart':
113 - case 'touchend': {
114 - isEmulatingMouseEvents = true;
115 - isGlobalFocusVisible = false;
116 - break;
117 - }
118 - case 'mousedown': {
119 - if (!isEmulatingMouseEvents) {
120 - isGlobalFocusVisible = false;
121 - } else {
122 - isEmulatingMouseEvents = false;
123 - }
124 - break;
125 - }
126 - }
127 -}
128 -
129 -const passiveObject = {passive: true};
130 -
131 -function handleFocusVisibleTargetEvent(
132 - type: string,
133 - focusTarget: EventTarget,
134 - callback: boolean => void,
135 -): void {
136 - isGlobalFocusVisible = false;
137 -
138 - // Focus should stop being visible if a pointer is used on the element
139 - // after it was focused using a keyboard.
140 - if (
141 - focusTarget !== null &&
142 - (type === 'mousedown' || type === 'touchstart' || type === 'pointerdown')
143 - ) {
144 - callback(false);
145 - }
146 -}
147 -
148 -function handleFocusVisibleTargetEvents(
149 - event: SyntheticEvent<EventTarget>,
150 - focusTarget,
151 - callback,
152 -): void {
153 - const {type} = event;
154 -
155 - switch (type) {
156 - case 'pointermove':
157 - case 'pointerdown':
158 - case 'pointerup': {
159 - handleFocusVisibleTargetEvent(type, focusTarget, callback);
160 - break;
161 - }
162 -
163 - case 'keydown':
164 - case 'keyup': {
165 - const {metaKey, altKey, ctrlKey} = (event: any);
166 - const validKey = !(metaKey || (!isMac && altKey) || ctrlKey);
167 -
168 - if (validKey) {
169 - if (focusTarget !== null) {
170 - callback(true);
171 - }
172 - }
173 - break;
174 - }
175 -
176 - // fallbacks for no PointerEvent support
177 - case 'touchmove':
178 - case 'touchstart':
179 - case 'touchend': {
180 - handleFocusVisibleTargetEvent(type, focusTarget, callback);
181 - break;
182 - }
183 - case 'mousedown': {
184 - if (!isEmulatingMouseEvents) {
185 - handleFocusVisibleTargetEvent(type, focusTarget, callback);
186 - }
187 - break;
188 - }
189 - }
190 -}
191 -
192 -function isRelatedTargetWithin(
193 - focusWithinTarget: Node,
194 - relatedTarget: null | Node,
195 -): boolean {
196 - if (relatedTarget == null) {
197 - return false;
198 - }
199 - // To support experimental scopes, which can be the target:
200 - const containsNode = (focusWithinTarget: any).containsNode;
201 - if (typeof containsNode === 'function') {
202 - return containsNode(relatedTarget);
203 - }
204 - return focusWithinTarget.contains(relatedTarget);
205 -}
206 -
207 -function setFocusVisibleListeners(focusVisibleHandles, focusTarget, callback) {
208 - focusVisibleHandles.forEach(focusVisibleHandle => {
209 - focusVisibleHandle.setListener(focusTarget, event =>
210 - handleFocusVisibleTargetEvents(event, focusTarget, callback),
211 - );
212 - });
213 -}
214 -
215 -function useFocusVisibleInputHandles() {
216 - return [
217 - useEvent('mousedown', passiveObject),
218 - useEvent(hasPointerEvents ? 'pointerdown' : 'touchstart', passiveObject),
219 - useEvent(hasPointerEvents ? 'pointermove' : 'touchmove', passiveObject),
220 - useEvent(hasPointerEvents ? 'pointerup' : 'touchend', passiveObject),
221 - useEvent('keydown', passiveObject),
222 - ];
223 -}
224 -
225 -function useFocusLifecycles(stateRef) {
226 - useEffect(() => {
227 - if (!hasTrackedGlobalFocusVisible) {
228 - hasTrackedGlobalFocusVisible = true;
229 - trackGlobalFocusVisible();
230 - }
231 - }, []);
232 -}
233 -
234 -export function useFocus(
235 - focusTargetRef: {current: null | Node},
236 - {
237 - disabled,
238 - onBlur,
239 - onFocus,
240 - onFocusChange,
241 - onFocusVisibleChange,
242 - }: UseFocusOptions,
243 -): void {
244 - // Setup controlled state for this useFocus hook
245 - const stateRef = useRef({isFocused: false, isFocusVisible: false});
246 - const focusHandle = useEvent('focus', passiveObject);
247 - const blurHandle = useEvent('blur', passiveObject);
248 - const focusVisibleHandles = useFocusVisibleInputHandles();
249 -
250 - useEffect(() => {
251 - const focusTarget = focusTargetRef.current;
252 - const state = stateRef.current;
253 -
254 - if (focusTarget !== null && state !== null) {
255 - // Handle focus visible
256 - setFocusVisibleListeners(
257 - focusVisibleHandles,
258 - focusTarget,
259 - isFocusVisible => {
260 - if (state.isFocused && state.isFocusVisible !== isFocusVisible) {
261 - state.isFocusVisible = isFocusVisible;
262 - if (onFocusVisibleChange) {
263 - onFocusVisibleChange(isFocusVisible);
264 - }
265 - }
266 - },
267 - );
268 -
269 - // Handle focus
270 - focusHandle.setListener(focusTarget, event => {
271 - if (disabled) {
272 - return;
273 - }
274 - // Limit focus events to the direct child of the event component.
275 - // Browser focus is not expected to bubble.
276 - if (!state.isFocused && focusTarget === event.target) {
277 - state.isFocused = true;
278 - state.isFocusVisible = isGlobalFocusVisible;
279 - if (onFocus) {
280 - onFocus(event);
281 - }
282 - if (onFocusChange) {
283 - onFocusChange(true);
284 - }
285 - if (state.isFocusVisible && onFocusVisibleChange) {
286 - onFocusVisibleChange(true);
287 - }
288 - isEmulatingMouseEvents = false;
289 - }
290 - });
291 -
292 - // Handle blur
293 - blurHandle.setListener(focusTarget, event => {
294 - if (disabled) {
295 - return;
296 - }
297 - if (state.isFocused) {
298 - state.isFocused = false;
299 - state.isFocusVisible = isGlobalFocusVisible;
300 - if (onBlur) {
301 - onBlur(event);
302 - }
303 - if (onFocusChange) {
304 - onFocusChange(false);
305 - }
306 - if (state.isFocusVisible && onFocusVisibleChange) {
307 - onFocusVisibleChange(false);
308 - }
309 - }
310 - isEmulatingMouseEvents = false;
311 - });
312 - }
313 - }, [disabled, onBlur, onFocus, onFocusChange, onFocusVisibleChange]);
314 -
315 - // Mount/Unmount logic
316 - useFocusLifecycles(stateRef);
317 -}
318 -
319 -export function useFocusWithin(
320 - focusWithinTargetRef: {current: null | Node},
321 - {
322 - disabled,
323 - onAfterBlurWithin,
324 - onBeforeBlurWithin,
325 - onBlurWithin,
326 - onFocusWithin,
327 - onFocusWithinChange,
328 - onFocusWithinVisibleChange,
329 - }: UseFocusWithinOptions,
330 -) {
331 - // Setup controlled state for this useFocus hook
332 - const stateRef = useRef({isFocused: false, isFocusVisible: false});
333 - const focusHandle = useEvent('focus', passiveObject);
334 - const blurHandle = useEvent('blur', passiveObject);
335 - const afterBlurHandle = useEvent('afterblur', passiveObject);
336 - const beforeBlurHandle = useEvent('beforeblur', passiveObject);
337 - const focusVisibleHandles = useFocusVisibleInputHandles();
338 -
339 - useEffect(() => {
340 - const focusWithinTarget = focusWithinTargetRef.current;
341 - const state = stateRef.current;
342 -
343 - if (focusWithinTarget !== null && state !== null) {
344 - // Handle focus visible
345 - setFocusVisibleListeners(
346 - focusVisibleHandles,
347 - focusWithinTarget,
348 - isFocusVisible => {
349 - if (state.isFocused && state.isFocusVisible !== isFocusVisible) {
350 - state.isFocusVisible = isFocusVisible;
351 - if (onFocusWithinVisibleChange) {
352 - onFocusWithinVisibleChange(isFocusVisible);
353 - }
354 - }
355 - },
356 - );
357 -
358 - // Handle focus
359 - focusHandle.setListener(focusWithinTarget, event => {
360 - if (disabled) {
361 - return;
362 - }
363 - if (!state.isFocused) {
364 - state.isFocused = true;
365 - state.isFocusVisible = isGlobalFocusVisible;
366 - if (onFocusWithinChange) {
367 - onFocusWithinChange(true);
368 - }
369 - if (state.isFocusVisible && onFocusWithinVisibleChange) {
370 - onFocusWithinVisibleChange(true);
371 - }
372 - }
373 - if (!state.isFocusVisible && isGlobalFocusVisible) {
374 - state.isFocusVisible = isGlobalFocusVisible;
375 - if (onFocusWithinVisibleChange) {
376 - onFocusWithinVisibleChange(true);
377 - }
378 - }
379 - if (onFocusWithin) {
380 - onFocusWithin(event);
381 - }
382 - isEmulatingMouseEvents = false;
383 - });
384 -
385 - // Handle blur
386 - blurHandle.setListener(focusWithinTarget, event => {
387 - if (disabled) {
388 - return;
389 - }
390 - const {relatedTarget} = (event: any);
391 -
392 - if (
393 - state.isFocused &&
394 - !isRelatedTargetWithin(focusWithinTarget, relatedTarget)
395 - ) {
396 - state.isFocused = false;
397 - if (onFocusWithinChange) {
398 - onFocusWithinChange(false);
399 - }
400 - if (state.isFocusVisible && onFocusWithinVisibleChange) {
401 - onFocusWithinVisibleChange(false);
402 - }
403 - if (onBlurWithin) {
404 - onBlurWithin(event);
405 - }
406 - }
407 - isEmulatingMouseEvents = false;
408 - });
409 -
410 - // Handle before blur. This is a special
411 - // React provided event.
412 - beforeBlurHandle.setListener(focusWithinTarget, event => {
413 - if (disabled) {
414 - return;
415 - }
416 - if (onBeforeBlurWithin) {
417 - onBeforeBlurWithin(event);
418 - // Add an "afterblur" listener on document. This is a special
419 - // React provided event.
420 - afterBlurHandle.setListener(document, afterBlurEvent => {
421 - if (onAfterBlurWithin) {
422 - onAfterBlurWithin(afterBlurEvent);
423 - }
424 - // Clear listener on document
425 - afterBlurHandle.setListener(document, null);
426 - });
427 - }
428 - });
429 - }
430 - }, [
431 - disabled,
432 - onBlurWithin,
433 - onFocusWithin,
434 - onFocusWithinChange,
435 - onFocusWithinVisibleChange,
436 - ]);
437 -
438 - // Mount/Unmount logic
439 - useFocusLifecycles(stateRef);
440 -}
packages/react-interactions/events/src/dom/use-event/__tests__/useFocus-test.internal.js deleted
-319
@@ -1,319 +0,0 @@
1 -/**
2 - * Copyright (c) Facebook, Inc. and its affiliates.
3 - *
4 - * This source code is licensed under the MIT license found in the
5 - * LICENSE file in the root directory of this source tree.
6 - *
7 - * @emails react-core
8 - */
9 -
10 -'use strict';
11 -
12 -import {createEventTarget, setPointerEvent} from 'dom-event-testing-library';
13 -
14 -let React;
15 -let ReactFeatureFlags;
16 -let ReactDOM;
17 -let useFocus;
18 -
19 -function initializeModules(hasPointerEvents) {
20 - setPointerEvent(hasPointerEvents);
21 - jest.resetModules();
22 - ReactFeatureFlags = require('shared/ReactFeatureFlags');
23 - ReactFeatureFlags.enableModernEventSystem = true;
24 - ReactFeatureFlags.enableUseEventAPI = true;
25 - React = require('react');
26 - ReactDOM = require('react-dom');
27 -
28 - // TODO: This import throws outside of experimental mode. Figure out better
29 - // strategy for gated imports.
30 - if (__EXPERIMENTAL__) {
31 - useFocus = require('react-interactions/events/focus').useFocus;
32 - }
33 -}
34 -
35 -const forcePointerEvents = true;
36 -const table = [[forcePointerEvents], [!forcePointerEvents]];
37 -
38 -describe.each(table)(`useFocus`, hasPointerEvents => {
39 - let container;
40 -
41 - beforeEach(() => {
42 - initializeModules(hasPointerEvents);
43 - container = document.createElement('div');
44 - document.body.appendChild(container);
45 - });
46 -
47 - afterEach(() => {
48 - ReactDOM.render(null, container);
49 - document.body.removeChild(container);
50 - container = null;
51 - });
52 -
53 - describe('disabled', () => {
54 - let onBlur, onFocus, ref;
55 -
56 - const componentInit = () => {
57 - onBlur = jest.fn();
58 - onFocus = jest.fn();
59 - ref = React.createRef();
60 - const Component = () => {
61 - useFocus(ref, {
62 - disabled: true,
63 - onBlur,
64 - onFocus,
65 - });
66 - return <div ref={ref} />;
67 - };
68 - ReactDOM.render(<Component />, container);
69 - };
70 -
71 - // @gate experimental
72 - it('does not call callbacks', () => {
73 - componentInit();
74 - const target = createEventTarget(ref.current);
75 - target.focus();
76 - target.blur();
77 - expect(onFocus).not.toBeCalled();
78 - expect(onBlur).not.toBeCalled();
79 - });
80 - });
81 -
82 - describe('onBlur', () => {
83 - let onBlur, ref;
84 -
85 - const componentInit = () => {
86 - onBlur = jest.fn();
87 - ref = React.createRef();
88 - const Component = () => {
89 - useFocus(ref, {
90 - onBlur,
91 - });
92 - return <div ref={ref} />;
93 - };
94 - ReactDOM.render(<Component />, container);
95 - };
96 -
97 - // @gate experimental
98 - it('is called after "blur" event', () => {
99 - componentInit();
100 - const target = createEventTarget(ref.current);
101 - target.focus();
102 - target.blur();
103 - expect(onBlur).toHaveBeenCalledTimes(1);
104 - });
105 - });
106 -
107 - describe('onFocus', () => {
108 - let onFocus, ref, innerRef;
109 -
110 - const componentInit = () => {
111 - onFocus = jest.fn();
112 - ref = React.createRef();
113 - innerRef = React.createRef();
114 - const Component = () => {
115 - useFocus(ref, {
116 - onFocus,
117 - });
118 - return (
119 - <div ref={ref}>
120 - <a ref={innerRef} />
121 - </div>
122 - );
123 - };
124 - ReactDOM.render(<Component />, container);
125 - };
126 -
127 - // @gate experimental
128 - it('is called after "focus" event', () => {
129 - componentInit();
130 - const target = createEventTarget(ref.current);
131 - target.focus();
132 - expect(onFocus).toHaveBeenCalledTimes(1);
133 - });
134 -
135 - // @gate experimental
136 - it('is not called if descendants of target receive focus', () => {
137 - componentInit();
138 - const target = createEventTarget(innerRef.current);
139 - target.focus();
140 - expect(onFocus).not.toBeCalled();
141 - });
142 - });
143 -
144 - describe('onFocusChange', () => {
145 - let onFocusChange, ref, innerRef;
146 -
147 - const componentInit = () => {
148 - onFocusChange = jest.fn();
149 - ref = React.createRef();
150 - innerRef = React.createRef();
151 - const Component = () => {
152 - useFocus(ref, {
153 - onFocusChange,
154 - });
155 - return (
156 - <div ref={ref}>
157 - <div ref={innerRef} />
158 - </div>
159 - );
160 - };
161 - ReactDOM.render(<Component />, container);
162 - };
163 -
164 - // @gate experimental
165 - it('is called after "blur" and "focus" events', () => {
166 - componentInit();
167 - const target = createEventTarget(ref.current);
168 - target.focus();
169 - expect(onFocusChange).toHaveBeenCalledTimes(1);
170 - expect(onFocusChange).toHaveBeenCalledWith(true);
171 - target.blur();
172 - expect(onFocusChange).toHaveBeenCalledTimes(2);
173 - expect(onFocusChange).toHaveBeenCalledWith(false);
174 - });
175 -
176 - // @gate experimental
177 - it('is not called after "blur" and "focus" events on descendants', () => {
178 - componentInit();
179 - const target = createEventTarget(innerRef.current);
180 - target.focus();
181 - expect(onFocusChange).toHaveBeenCalledTimes(0);
182 - target.blur();
183 - expect(onFocusChange).toHaveBeenCalledTimes(0);
184 - });
185 - });
186 -
187 - describe('onFocusVisibleChange', () => {
188 - let onFocusVisibleChange, ref, innerRef;
189 -
190 - const componentInit = () => {
191 - onFocusVisibleChange = jest.fn();
192 - ref = React.createRef();
193 - innerRef = React.createRef();
194 - const Component = () => {
195 - useFocus(ref, {
196 - onFocusVisibleChange,
197 - });
198 - return (
199 - <div ref={ref}>
200 - <div ref={innerRef} />
201 - </div>
202 - );
203 - };
204 - ReactDOM.render(<Component />, container);
205 - };
206 -
207 - // @gate experimental
208 - it('is called after "focus" and "blur" if keyboard navigation is active', () => {
209 - componentInit();
210 - const target = createEventTarget(ref.current);
211 - const containerTarget = createEventTarget(container);
212 - // use keyboard first
213 - containerTarget.keydown({key: 'Tab'});
214 - target.focus();
215 - expect(onFocusVisibleChange).toHaveBeenCalledTimes(1);
216 - expect(onFocusVisibleChange).toHaveBeenCalledWith(true);
217 - target.blur({relatedTarget: container});
218 - expect(onFocusVisibleChange).toHaveBeenCalledTimes(2);
219 - expect(onFocusVisibleChange).toHaveBeenCalledWith(false);
220 - });
221 -
222 - // @gate experimental
223 - it('is called if non-keyboard event is dispatched on target previously focused with keyboard', () => {
224 - componentInit();
225 - const target = createEventTarget(ref.current);
226 - const containerTarget = createEventTarget(container);
227 - // use keyboard first
228 - containerTarget.keydown({key: 'Tab'});
229 - target.focus();
230 - expect(onFocusVisibleChange).toHaveBeenCalledTimes(1);
231 - expect(onFocusVisibleChange).toHaveBeenCalledWith(true);
232 - // then use pointer on the target, focus should no longer be visible
233 - target.pointerdown();
234 - expect(onFocusVisibleChange).toHaveBeenCalledTimes(2);
235 - expect(onFocusVisibleChange).toHaveBeenCalledWith(false);
236 - // onFocusVisibleChange should not be called again
237 - target.blur({relatedTarget: container});
238 - expect(onFocusVisibleChange).toHaveBeenCalledTimes(2);
239 - });
240 -
241 - // @gate experimental
242 - it('is not called after "focus" and "blur" events without keyboard', () => {
243 - componentInit();
244 - const target = createEventTarget(ref.current);
245 - const containerTarget = createEventTarget(container);
246 - target.pointerdown();
247 - target.pointerup();
248 - containerTarget.pointerdown();
249 - target.blur({relatedTarget: container});
250 - expect(onFocusVisibleChange).toHaveBeenCalledTimes(0);
251 - });
252 -
253 - // @gate experimental
254 - it('is not called after "blur" and "focus" events on descendants', () => {
255 - componentInit();
256 - const innerTarget = createEventTarget(innerRef.current);
257 - const containerTarget = createEventTarget(container);
258 - containerTarget.keydown({key: 'Tab'});
259 - innerTarget.focus();
260 - expect(onFocusVisibleChange).toHaveBeenCalledTimes(0);
261 - innerTarget.blur({relatedTarget: container});
262 - expect(onFocusVisibleChange).toHaveBeenCalledTimes(0);
263 - });
264 - });
265 -
266 - describe('nested Focus components', () => {
267 - // @gate experimental
268 - it('propagates events in the correct order', () => {
269 - const events = [];
270 - const innerRef = React.createRef();
271 - const outerRef = React.createRef();
272 - const createEventHandler = msg => () => {
273 - events.push(msg);
274 - };
275 -
276 - const Inner = () => {
277 - useFocus(innerRef, {
278 - onBlur: createEventHandler('inner: onBlur'),
279 - onFocus: createEventHandler('inner: onFocus'),
280 - onFocusChange: createEventHandler('inner: onFocusChange'),
281 - });
282 - return <div ref={innerRef} />;
283 - };
284 -
285 - const Outer = () => {
286 - useFocus(outerRef, {
287 - onBlur: createEventHandler('outer: onBlur'),
288 - onFocus: createEventHandler('outer: onFocus'),
289 - onFocusChange: createEventHandler('outer: onFocusChange'),
290 - });
291 - return (
292 - <div ref={outerRef}>
293 - <Inner />
294 - </div>
295 - );
296 - };
297 -
298 - ReactDOM.render(<Outer />, container);
299 -
300 - const innerTarget = createEventTarget(innerRef.current);
301 - const outerTarget = createEventTarget(outerRef.current);
302 -
303 - outerTarget.focus();
304 - outerTarget.blur();
305 - innerTarget.focus();
306 - innerTarget.blur();
307 - expect(events).toEqual([
308 - 'outer: onFocus',
309 - 'outer: onFocusChange',
310 - 'outer: onBlur',
311 - 'outer: onFocusChange',
312 - 'inner: onFocus',
313 - 'inner: onFocusChange',
314 - 'inner: onBlur',
315 - 'inner: onFocusChange',
316 - ]);
317 - });
318 - });
319 -});
packages/react-interactions/events/src/dom/use-event/__tests__/useFocusWithin-test.internal.js deleted
-569
@@ -1,569 +0,0 @@
1 -/**
2 - * Copyright (c) Facebook, Inc. and its affiliates.
3 - *
4 - * This source code is licensed under the MIT license found in the
5 - * LICENSE file in the root directory of this source tree.
6 - *
7 - * @emails react-core
8 - */
9 -
10 -'use strict';
11 -
12 -import {createEventTarget, setPointerEvent} from 'dom-event-testing-library';
13 -
14 -let React;
15 -let ReactFeatureFlags;
16 -let ReactDOM;
17 -let useFocusWithin;
18 -let ReactTestRenderer;
19 -let act;
20 -
21 -function initializeModules(hasPointerEvents) {
22 - setPointerEvent(hasPointerEvents);
23 - jest.resetModules();
24 - ReactFeatureFlags = require('shared/ReactFeatureFlags');
25 - ReactFeatureFlags.enableScopeAPI = true;
26 - ReactFeatureFlags.enableModernEventSystem = true;
27 - ReactFeatureFlags.enableUseEventAPI = true;
28 - React = require('react');
29 - ReactDOM = require('react-dom');
30 - ReactTestRenderer = require('react-test-renderer');
31 - act = ReactTestRenderer.act;
32 -
33 - // TODO: This import throws outside of experimental mode. Figure out better
34 - // strategy for gated imports.
35 - if (__EXPERIMENTAL__) {
36 - useFocusWithin = require('react-interactions/events/focus').useFocusWithin;
37 - }
38 -}
39 -
40 -const forcePointerEvents = true;
41 -const table = [[forcePointerEvents], [!forcePointerEvents]];
42 -
43 -describe.each(table)(`useFocus`, hasPointerEvents => {
44 - let container;
45 - let container2;
46 -
47 - beforeEach(() => {
48 - initializeModules(hasPointerEvents);
49 - container = document.createElement('div');
50 - document.body.appendChild(container);
51 - container2 = document.createElement('div');
52 - document.body.appendChild(container2);
53 - });
54 -
55 - afterEach(() => {
56 - ReactDOM.render(null, container);
57 - document.body.removeChild(container);
58 - document.body.removeChild(container2);
59 - container = null;
60 - container2 = null;
61 - });
62 -
63 - describe('disabled', () => {
64 - let onFocusWithinChange, onFocusWithinVisibleChange, ref;
65 -
66 - const componentInit = () => {
67 - onFocusWithinChange = jest.fn();
68 - onFocusWithinVisibleChange = jest.fn();
69 - ref = React.createRef();
70 - const Component = () => {
71 - useFocusWithin(ref, {
72 - disabled: true,
73 - onFocusWithinChange,
74 - onFocusWithinVisibleChange,
75 - });
76 - return <div ref={ref} />;
77 - };
78 - ReactDOM.render(<Component />, container);
79 - };
80 -
81 - // @gate experimental
82 - it('prevents custom events being dispatched', () => {
83 - componentInit();
84 - const target = createEventTarget(ref.current);
85 - target.focus();
86 - target.blur();
87 - expect(onFocusWithinChange).not.toBeCalled();
88 - expect(onFocusWithinVisibleChange).not.toBeCalled();
89 - });
90 - });
91 -
92 - describe('onFocusWithinChange', () => {
93 - let onFocusWithinChange, ref, innerRef, innerRef2;
94 -
95 - const Component = ({show}) => {
96 - useFocusWithin(ref, {
97 - onFocusWithinChange,
98 - });
99 - return (
100 - <div ref={ref}>
101 - {show && <input ref={innerRef} />}
102 - <div ref={innerRef2} />
103 - </div>
104 - );
105 - };
106 -
107 - const componentInit = () => {
108 - onFocusWithinChange = jest.fn();
109 - ref = React.createRef();
110 - innerRef = React.createRef();
111 - innerRef2 = React.createRef();
112 - ReactDOM.render(<Component show={true} />, container);
113 - };
114 -
115 - // @gate experimental
116 - it('is called after "blur" and "focus" events on focus target', () => {
117 - componentInit();
118 - const target = createEventTarget(ref.current);
119 - target.focus();
120 - expect(onFocusWithinChange).toHaveBeenCalledTimes(1);
121 - expect(onFocusWithinChange).toHaveBeenCalledWith(true);
122 - target.blur({relatedTarget: container});
123 - expect(onFocusWithinChange).toHaveBeenCalledTimes(2);
124 - expect(onFocusWithinChange).toHaveBeenCalledWith(false);
125 - });
126 -
127 - // @gate experimental
128 - it('is called after "blur" and "focus" events on descendants', () => {
129 - componentInit();
130 - const target = createEventTarget(innerRef.current);
131 - target.focus();
132 - expect(onFocusWithinChange).toHaveBeenCalledTimes(1);
133 - expect(onFocusWithinChange).toHaveBeenCalledWith(true);
134 - target.blur({relatedTarget: container});
135 - expect(onFocusWithinChange).toHaveBeenCalledTimes(2);
136 - expect(onFocusWithinChange).toHaveBeenCalledWith(false);
137 - });
138 -
139 - // @gate experimental
140 - it('is only called once when focus moves within and outside the subtree', () => {
141 - componentInit();
142 - const node = ref.current;
143 - const innerNode1 = innerRef.current;
144 - const innerNode2 = innerRef.current;
145 - const target = createEventTarget(node);
146 - const innerTarget1 = createEventTarget(innerNode1);
147 - const innerTarget2 = createEventTarget(innerNode2);
148 -
149 - // focus shifts into subtree
150 - innerTarget1.focus();
151 - expect(onFocusWithinChange).toHaveBeenCalledTimes(1);
152 - expect(onFocusWithinChange).toHaveBeenCalledWith(true);
153 - // focus moves around subtree
154 - innerTarget1.blur({relatedTarget: innerNode2});
155 - innerTarget2.focus();
156 - innerTarget2.blur({relatedTarget: node});
157 - target.focus();
158 - target.blur({relatedTarget: innerNode1});
159 - expect(onFocusWithinChange).toHaveBeenCalledTimes(1);
160 - // focus shifts outside subtree
161 - innerTarget1.blur({relatedTarget: container});
162 - expect(onFocusWithinChange).toHaveBeenCalledTimes(2);
163 - expect(onFocusWithinChange).toHaveBeenCalledWith(false);
164 - });
165 - });
166 -
167 - describe('onFocusWithinVisibleChange', () => {
168 - let onFocusWithinVisibleChange, ref, innerRef, innerRef2;
169 -
170 - const Component = ({show}) => {
171 - useFocusWithin(ref, {
172 - onFocusWithinVisibleChange,
173 - });
174 - return (
175 - <div ref={ref}>
176 - {show && <input ref={innerRef} />}
177 - <div ref={innerRef2} />
178 - </div>
179 - );
180 - };
181 -
182 - const componentInit = () => {
183 - onFocusWithinVisibleChange = jest.fn();
184 - ref = React.createRef();
185 - innerRef = React.createRef();
186 - innerRef2 = React.createRef();
187 - ReactDOM.render(<Component show={true} />, container);
188 - };
189 -
190 - // @gate experimental
191 - it('is called after "focus" and "blur" on focus target if keyboard was used', () => {
192 - componentInit();
193 - const target = createEventTarget(ref.current);
194 - const containerTarget = createEventTarget(container);
195 - // use keyboard first
196 - containerTarget.keydown({key: 'Tab'});
197 - target.focus();
198 - expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(1);
199 - expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(true);
200 - target.blur({relatedTarget: container});
201 - expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(2);
202 - expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(false);
203 - });
204 -
205 - // @gate experimental
206 - it('is called after "focus" and "blur" on descendants if keyboard was used', () => {
207 - componentInit();
208 - const innerTarget = createEventTarget(innerRef.current);
209 - const containerTarget = createEventTarget(container);
210 - // use keyboard first
211 - containerTarget.keydown({key: 'Tab'});
212 - innerTarget.focus();
213 - expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(1);
214 - expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(true);
215 - innerTarget.blur({relatedTarget: container});
216 - expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(2);
217 - expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(false);
218 - });
219 -
220 - // @gate experimental
221 - it('is called if non-keyboard event is dispatched on target previously focused with keyboard', () => {
222 - componentInit();
223 - const node = ref.current;
224 - const innerNode1 = innerRef.current;
225 - const innerNode2 = innerRef2.current;
226 -
227 - const target = createEventTarget(node);
228 - const innerTarget1 = createEventTarget(innerNode1);
229 - const innerTarget2 = createEventTarget(innerNode2);
230 - // use keyboard first
231 - target.focus();
232 - target.keydown({key: 'Tab'});
233 - target.blur({relatedTarget: innerNode1});
234 - innerTarget1.focus();
235 - expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(1);
236 - expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(true);
237 - // then use pointer on the next target, focus should no longer be visible
238 - innerTarget2.pointerdown();
239 - innerTarget1.blur({relatedTarget: innerNode2});
240 - innerTarget2.focus();
241 - expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(2);
242 - expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(false);
243 - // then use keyboard again
244 - innerTarget2.keydown({key: 'Tab', shiftKey: true});
245 - innerTarget2.blur({relatedTarget: innerNode1});
246 - innerTarget1.focus();
247 - expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(3);
248 - expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(true);
249 - // then use pointer on the target, focus should no longer be visible
250 - innerTarget1.pointerdown();
251 - expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(4);
252 - expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(false);
253 - // onFocusVisibleChange should not be called again
254 - innerTarget1.blur({relatedTarget: container});
255 - expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(4);
256 - });
257 -
258 - // @gate experimental
259 - it('is not called after "focus" and "blur" events without keyboard', () => {
260 - componentInit();
261 - const innerTarget = createEventTarget(innerRef.current);
262 - innerTarget.pointerdown();
263 - innerTarget.pointerup();
264 - innerTarget.blur({relatedTarget: container});
265 - expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(0);
266 - });
267 -
268 - // @gate experimental
269 - it('is only called once when focus moves within and outside the subtree', () => {
270 - componentInit();
271 - const node = ref.current;
272 - const innerNode1 = innerRef.current;
273 - const innerNode2 = innerRef2.current;
274 - const target = createEventTarget(node);
275 - const innerTarget1 = createEventTarget(innerNode1);
276 - const innerTarget2 = createEventTarget(innerNode2);
277 -
278 - // focus shifts into subtree
279 - innerTarget1.focus();
280 - expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(1);
281 - expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(true);
282 - // focus moves around subtree
283 - innerTarget1.blur({relatedTarget: innerNode2});
284 - innerTarget2.focus();
285 - innerTarget2.blur({relatedTarget: node});
286 - target.focus();
287 - target.blur({relatedTarget: innerNode1});
288 - expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(1);
289 - // focus shifts outside subtree
290 - innerTarget1.blur({relatedTarget: container});
291 - expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(2);
292 - expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(false);
293 - });
294 - });
295 -
296 - describe('onBeforeBlurWithin', () => {
297 - let onBeforeBlurWithin, onAfterBlurWithin, ref, innerRef, innerRef2;
298 -
299 - beforeEach(() => {
300 - onBeforeBlurWithin = jest.fn();
301 - onAfterBlurWithin = jest.fn(e => {
302 - e.persist();
303 - });
304 - ref = React.createRef();
305 - innerRef = React.createRef();
306 - innerRef2 = React.createRef();
307 - });
308 -
309 - // @gate experimental
310 - it('is called after a focused element is unmounted', () => {
311 - const Component = ({show}) => {
312 - useFocusWithin(ref, {
313 - onBeforeBlurWithin,
314 - onAfterBlurWithin,
315 - });
316 - return (
317 - <div ref={ref}>
318 - {show && <input ref={innerRef} />}
319 - <div ref={innerRef2} />
320 - </div>
321 - );
322 - };
323 -
324 - ReactDOM.render(<Component show={true} />, container);
325 -
326 - const inner = innerRef.current;
327 - const target = createEventTarget(inner);
328 - target.keydown({key: 'Tab'});
329 - target.focus();
330 - expect(onBeforeBlurWithin).toHaveBeenCalledTimes(0);
331 - expect(onAfterBlurWithin).toHaveBeenCalledTimes(0);
332 - ReactDOM.render(<Component show={false} />, container);
333 - expect(onBeforeBlurWithin).toHaveBeenCalledTimes(1);
334 - expect(onAfterBlurWithin).toHaveBeenCalledTimes(1);
335 - expect(onAfterBlurWithin).toHaveBeenCalledWith(
336 - expect.objectContaining({relatedTarget: inner}),
337 - );
338 - });
339 -
340 - // @gate experimental
341 - it('is called after a nested focused element is unmounted', () => {
342 - const Component = ({show}) => {
343 - useFocusWithin(ref, {
344 - onBeforeBlurWithin,
345 - onAfterBlurWithin,
346 - });
347 - return (
348 - <div ref={ref}>
349 - {show && (
350 - <div>
351 - <input ref={innerRef} />
352 - </div>
353 - )}
354 - <div ref={innerRef2} />
355 - </div>
356 - );
357 - };
358 -
359 - ReactDOM.render(<Component show={true} />, container);
360 -
361 - const inner = innerRef.current;
362 - const target = createEventTarget(inner);
363 - target.keydown({key: 'Tab'});
364 - target.focus();
365 - expect(onBeforeBlurWithin).toHaveBeenCalledTimes(0);
366 - expect(onAfterBlurWithin).toHaveBeenCalledTimes(0);
367 - ReactDOM.render(<Component show={false} />, container);
368 - expect(onBeforeBlurWithin).toHaveBeenCalledTimes(1);
369 - expect(onAfterBlurWithin).toHaveBeenCalledTimes(1);
370 - expect(onAfterBlurWithin).toHaveBeenCalledWith(
371 - expect.objectContaining({relatedTarget: inner}),
372 - );
373 - });
374 -
375 - // @gate experimental
376 - it('is called after many elements are unmounted', () => {
377 - const buttonRef = React.createRef();
378 - const inputRef = React.createRef();
379 -
380 - const Component = ({show}) => {
381 - useFocusWithin(ref, {
382 - onBeforeBlurWithin,
383 - onAfterBlurWithin,
384 - });
385 - return (
386 - <div ref={ref}>
387 - {show && <button>Press me!</button>}
388 - {show && <button>Press me!</button>}
389 - {show && <input ref={inputRef} />}
390 - {show && <button>Press me!</button>}
391 - {!show && <button ref={buttonRef}>Press me!</button>}
392 - {show && <button>Press me!</button>}
393 - <button>Press me!</button>
394 - <button>Press me!</button>
395 - </div>
396 - );
397 - };
398 -
399 - ReactDOM.render(<Component show={true} />, container);
400 -
401 - inputRef.current.focus();
402 - expect(onBeforeBlurWithin).toHaveBeenCalledTimes(0);
403 - expect(onAfterBlurWithin).toHaveBeenCalledTimes(0);
404 - ReactDOM.render(<Component show={false} />, container);
405 - expect(onBeforeBlurWithin).toHaveBeenCalledTimes(1);
406 - expect(onAfterBlurWithin).toHaveBeenCalledTimes(1);
407 - });
408 -
409 - // @gate experimental
410 - it('is called after a nested focused element is unmounted (with scope query)', () => {
411 - const TestScope = React.unstable_createScope();
412 - const testScopeQuery = (type, props) => true;
413 - let targetNodes;
414 - let targetNode;
415 -
416 - const Component = ({show}) => {
417 - const scopeRef = React.useRef(null);
418 - useFocusWithin(scopeRef, {
419 - onBeforeBlurWithin(event) {
420 - const scope = scopeRef.current;
421 - targetNode = innerRef.current;
422 - targetNodes = scope.DO_NOT_USE_queryAllNodes(testScopeQuery);
423 - },
424 - });
425 -
426 - return (
427 - <TestScope ref={scopeRef}>
428 - {show && <input ref={innerRef} />}
429 - </TestScope>
430 - );
431 - };
432 -
433 - ReactDOM.render(<Component show={true} />, container);
434 -
435 - const inner = innerRef.current;
436 - const target = createEventTarget(inner);
437 - target.keydown({key: 'Tab'});
438 - target.focus();
439 - ReactDOM.render(<Component show={false} />, container);
440 - expect(targetNodes).toEqual([targetNode]);
441 - });
442 -
443 - // @gate experimental
444 - it('is called after a focused suspended element is hidden', () => {
445 - const Suspense = React.Suspense;
446 - let suspend = false;
447 - let resolve;
448 - const promise = new Promise(resolvePromise => (resolve = resolvePromise));
449 -
450 - function Child() {
451 - if (suspend) {
452 - throw promise;
453 - } else {
454 - return <input ref={innerRef} />;
455 - }
456 - }
457 -
458 - const Component = ({show}) => {
459 - useFocusWithin(ref, {
460 - onBeforeBlurWithin,
461 - onAfterBlurWithin,
462 - });
463 -
464 - return (
465 - <div ref={ref}>
466 - <Suspense fallback="Loading...">
467 - <Child />
468 - </Suspense>
469 - </div>
470 - );
471 - };
472 -
473 - const root = ReactDOM.createRoot(container2);
474 -
475 - act(() => {
476 - root.render(<Component />);
477 - });
478 - jest.runAllTimers();
479 - expect(container2.innerHTML).toBe('<div><input></div>');
480 -
481 - const inner = innerRef.current;
482 - const target = createEventTarget(inner);
483 - target.keydown({key: 'Tab'});
484 - target.focus();
485 - expect(onBeforeBlurWithin).toHaveBeenCalledTimes(0);
486 - expect(onAfterBlurWithin).toHaveBeenCalledTimes(0);
487 -
488 - suspend = true;
489 - act(() => {
490 - root.render(<Component />);
491 - });
492 - jest.runAllTimers();
493 - expect(container2.innerHTML).toBe(
494 - '<div><input style="display: none;">Loading...</div>',
495 - );
496 - expect(onBeforeBlurWithin).toHaveBeenCalledTimes(1);
497 - expect(onAfterBlurWithin).toHaveBeenCalledTimes(1);
498 - resolve();
499 - });
500 -
501 - // @gate experimental
502 - it('is called after a focused suspended element is hidden then shown', () => {
503 - const Suspense = React.Suspense;
504 - let suspend = false;
505 - let resolve;
506 - const promise = new Promise(resolvePromise => (resolve = resolvePromise));
507 - const buttonRef = React.createRef();
508 -
509 - function Child() {
510 - if (suspend) {
511 - throw promise;
512 - } else {
513 - return <input ref={innerRef} />;
514 - }
515 - }
516 -
517 - const Component = ({show}) => {
518 - useFocusWithin(ref, {
519 - onBeforeBlurWithin,
520 - onAfterBlurWithin,
521 - });
522 -
523 - return (
524 - <div ref={ref}>
525 - <Suspense fallback={<button ref={buttonRef}>Loading...</button>}>
526 - <Child />
527 - </Suspense>
528 - </div>
529 - );
530 - };
531 -
532 - const root = ReactDOM.createRoot(container2);
533 -
534 - act(() => {
535 - root.render(<Component />);
536 - });
537 - jest.runAllTimers();
538 -
539 - expect(onBeforeBlurWithin).toHaveBeenCalledTimes(0);
540 - expect(onAfterBlurWithin).toHaveBeenCalledTimes(0);
541 -
542 - suspend = true;
543 - act(() => {
544 - root.render(<Component />);
545 - });
546 - jest.runAllTimers();
547 - expect(onBeforeBlurWithin).toHaveBeenCalledTimes(0);
548 - expect(onAfterBlurWithin).toHaveBeenCalledTimes(0);
549 -
550 - act(() => {
551 - root.render(<Component />);
552 - });
553 - jest.runAllTimers();
554 - expect(onBeforeBlurWithin).toHaveBeenCalledTimes(0);
555 - expect(onAfterBlurWithin).toHaveBeenCalledTimes(0);
556 -
557 - buttonRef.current.focus();
558 - suspend = false;
559 - act(() => {
560 - root.render(<Component />);
561 - });
562 - jest.runAllTimers();
563 - expect(onBeforeBlurWithin).toHaveBeenCalledTimes(1);
564 - expect(onAfterBlurWithin).toHaveBeenCalledTimes(1);
565 -
566 - resolve();
567 - });
568 - });
569 -});
packages/react-native-renderer/src/ReactFabricHostConfig.js
-19
@@ -77,9 +77,6 @@ export type UpdatePayload = Object;
77 export type TimeoutHandle = TimeoutID;
78 export type NoTimeout = -1;
79
80 -export type ReactListenerEvent = Object;
81 -export type ReactListenerMap = Object;
82 -export type ReactListener = Object;
80 export type OpaqueIDType = void;
81
82 export type RendererInspectionConfig = $ReadOnly<{|
@@ -510,22 +507,6 @@ export function makeServerId(): OpaqueIDType {
507 throw new Error('Not yet implemented');
508 }
509
513 -export function registerEvent(event: any, rootContainerInstance: Container) {
514 - throw new Error('Not yet implemented.');
515 -}
516 -
517 -export function mountEventListener(listener: any) {
518 - throw new Error('Not yet implemented.');
519 -}
520 -
521 -export function unmountEventListener(listener: any) {
522 - throw new Error('Not yet implemented.');
523 -}
524 -
525 -export function validateEventListenerTarget(target: any, listener: any) {
526 - throw new Error('Not yet implemented.');
527 -}
528 -
510 export function beforeActiveInstanceBlur() {
511 // noop
512 }
packages/react-native-renderer/src/ReactNativeHostConfig.js
-20
@@ -28,10 +28,6 @@ import ReactNativeFiberHostComponent from './ReactNativeFiberHostComponent';
28
29 const {get: getViewConfigForType} = ReactNativeViewConfigRegistry;
30
31 -export type ReactListenerEvent = Object;
32 -export type ReactListenerMap = Object;
33 -export type ReactListener = Object;
34 -
31 export type Type = string;
32 export type Props = Object;
33 export type Container = number;
@@ -559,22 +555,6 @@ export function makeServerId(): OpaqueIDType {
555 throw new Error('Not yet implemented');
556 }
557
562 -export function registerEvent(event: any, rootContainerInstance: Container) {
563 - throw new Error('Not yet implemented.');
564 -}
565 -
566 -export function mountEventListener(listener: any) {
567 - throw new Error('Not yet implemented.');
568 -}
569 -
570 -export function unmountEventListener(listener: any) {
571 - throw new Error('Not yet implemented.');
572 -}
573 -
574 -export function validateEventListenerTarget(target: any, listener: any) {
575 - throw new Error('Not yet implemented.');
576 -}
577 -
558 export function beforeActiveInstanceBlur() {
559 // noop
560 }
packages/react-reconciler/src/ReactFiberCommitWork.new.js
+1 -4
@@ -36,7 +36,6 @@ import {
36 enableSuspenseCallback,
37 enableScopeAPI,
38 runAllPassiveEffectDestroysBeforeCreates,
39 - enableUseEventAPI,
39 } from 'shared/ReactFeatureFlags';
40 import {
41 FunctionComponent,
@@ -1020,9 +1019,7 @@ function commitUnmount(
1019 if (enableDeprecatedFlareAPI) {
1020 unmountDeprecatedResponderListeners(current);
1021 }
1023 - if (enableDeprecatedFlareAPI || enableUseEventAPI) {
1024 - beforeRemoveInstance(current.stateNode);
1025 - }
1022 + beforeRemoveInstance(current.stateNode);
1023 safelyDetachRef(current);
1024 return;
1025 }
packages/react-reconciler/src/ReactFiberCommitWork.old.js
+1 -4
@@ -36,7 +36,6 @@ import {
36 enableSuspenseCallback,
37 enableScopeAPI,
38 runAllPassiveEffectDestroysBeforeCreates,
39 - enableUseEventAPI,
39 } from 'shared/ReactFeatureFlags';
40 import {
41 FunctionComponent,
@@ -1020,9 +1019,7 @@ function commitUnmount(
1019 if (enableDeprecatedFlareAPI) {
1020 unmountDeprecatedResponderListeners(current);
1021 }
1023 - if (enableDeprecatedFlareAPI || enableUseEventAPI) {
1024 - beforeRemoveInstance(current.stateNode);
1025 - }
1022 + beforeRemoveInstance(current.stateNode);
1023 safelyDetachRef(current);
1024 return;
1025 }
packages/react-reconciler/src/ReactFiberHooks.new.js
+2 -211
@@ -14,7 +14,6 @@ import type {
14 ReactEventResponder,
15 ReactContext,
16 ReactEventResponderListener,
17 - ReactScopeMethods,
17 } from 'shared/ReactTypes';
18 import type {Fiber, Dispatcher} from './ReactInternalTypes';
19 import type {ExpirationTime} from './ReactFiberExpirationTime.new';
@@ -22,15 +21,10 @@ import type {HookEffectTag} from './ReactHookEffectTags';
21 import type {SuspenseConfig} from './ReactFiberSuspenseConfig';
22 import type {ReactPriorityLevel} from './ReactInternalTypes';
23 import type {FiberRoot} from './ReactInternalTypes';
25 -import type {
26 - OpaqueIDType,
27 - ReactListenerEvent,
28 - ReactListenerMap,
29 - ReactListener,
30 -} from './ReactFiberHostConfig';
24 +import type {OpaqueIDType} from './ReactFiberHostConfig';
25
26 import ReactSharedInternals from 'shared/ReactSharedInternals';
33 -import {enableDebugTracing, enableUseEventAPI} from 'shared/ReactFeatureFlags';
27 +import {enableDebugTracing} from 'shared/ReactFeatureFlags';
28
29 import {markRootExpiredAtTime} from './ReactFiberRoot.new';
30 import {
@@ -46,7 +40,6 @@ import {
40 Passive as PassiveEffect,
41 } from './ReactSideEffectTags';
42 import {
49 - NoEffect as NoHookEffect,
43 HasEffect as HookHasEffect,
44 Layout as HookLayout,
45 Passive as HookPassive,
@@ -63,12 +56,6 @@ import {
56 markUnprocessedUpdateTime,
57 priorityLevelToLabel,
58 } from './ReactFiberWorkLoop.new';
66 -import {
67 - registerEvent,
68 - mountEventListener as mountHostEventListener,
69 - unmountEventListener as unmountHostEventListener,
70 - validateEventListenerTarget,
71 -} from './ReactFiberHostConfig';
59
60 import invariant from 'shared/invariant';
61 import getComponentName from 'shared/getComponentName';
@@ -95,7 +82,6 @@ import {
82 setWorkInProgressVersion,
83 warnAboutMultipleRenderersDEV,
84 } from './ReactMutableSource.new';
98 -import {getRootHostContainer} from './ReactFiberHostContext.new';
85 import {getIsRendering} from './ReactCurrentFiber';
86 import {logStateUpdateScheduled} from './DebugTracing';
87
@@ -136,7 +122,6 @@ export type HookType =
122 | 'useDeferredValue'
123 | 'useTransition'
124 | 'useMutableSource'
139 - | 'useEvent'
125 | 'useOpaqueIdentifier';
126
127 let didWarnAboutMismatchedHooksForComponent;
@@ -1760,158 +1745,6 @@ function dispatchAction<S, A>(
1745 }
1746 }
1747
1763 -const noOpMount = () => {};
1764 -
1765 -function validateNotInFunctionRender(): boolean {
1766 - if (currentlyRenderingFiber === null) {
1767 - return true;
1768 - }
1769 - if (__DEV__) {
1770 - console.warn(
1771 - 'Event listener methods from useEvent() cannot be called during render.' +
1772 - ' These methods should be called in an effect or event callback outside the render.',
1773 - );
1774 - }
1775 - return false;
1776 -}
1777 -
1778 -function createReactListener(
1779 - event: ReactListenerEvent,
1780 - callback: (SyntheticEvent<EventTarget>) => void,
1781 - target: EventTarget | ReactScopeMethods,
1782 - destroy: Node => void,
1783 -): ReactListener {
1784 - return {
1785 - callback,
1786 - destroy,
1787 - event,
1788 - target,
1789 - };
1790 -}
1791 -
1792 -function mountEventListener(event: ReactListenerEvent): ReactListenerMap {
1793 - if (enableUseEventAPI) {
1794 - const hook = mountWorkInProgressHook();
1795 - const listenerMap: Map<
1796 - EventTarget | ReactScopeMethods,
1797 - ReactListener,
1798 - > = new Map();
1799 - const rootContainerInstance = getRootHostContainer();
1800 -
1801 - // Register the event to the current root to ensure event
1802 - // replaying can pick up the event ahead of time.
1803 - registerEvent(event, rootContainerInstance);
1804 -
1805 - const clear = () => {
1806 - if (validateNotInFunctionRender()) {
1807 - const listeners = Array.from(listenerMap.values());
1808 - for (let i = 0; i < listeners.length; i++) {
1809 - unmountHostEventListener(listeners[i]);
1810 - }
1811 - listenerMap.clear();
1812 - }
1813 - };
1814 -
1815 - const destroy = (target: Node) => {
1816 - // We don't need to call detachListenerFromInstance
1817 - // here as this method should only ever be called
1818 - // from renderers that need to remove the instance
1819 - // from the map representing an instance that still
1820 - // holds a reference to the listenerMap. This means
1821 - // things like "window" listeners on ReactDOM should
1822 - // never enter this call path as the the instance in
1823 - // those cases would be that of "window", which
1824 - // should be handled via an optimized route in the
1825 - // renderer, making less overhead here. If we change
1826 - // this heuristic we should update this path to make
1827 - // sure we call detachListenerFromInstance.
1828 - listenerMap.delete(target);
1829 - };
1830 -
1831 - const reactListenerMap: ReactListenerMap = {
1832 - clear,
1833 - setListener(
1834 - target: EventTarget | ReactScopeMethods,
1835 - callback: ?(SyntheticEvent<EventTarget>) => void,
1836 - ): void {
1837 - if (
1838 - validateNotInFunctionRender() &&
1839 - validateEventListenerTarget(target, callback)
1840 - ) {
1841 - let listener = listenerMap.get(target);
1842 - if (listener === undefined) {
1843 - if (callback == null) {
1844 - return;
1845 - }
1846 - listener = createReactListener(event, callback, target, destroy);
1847 - listenerMap.set(target, listener);
1848 - } else {
1849 - if (callback == null) {
1850 - listenerMap.delete(target);
1851 - unmountHostEventListener(listener);
1852 - return;
1853 - }
1854 - listener.callback = callback;
1855 - }
1856 - mountHostEventListener(listener);
1857 - }
1858 - },
1859 - };
1860 - // In order to clear up upon the hook unmounting,
1861 - // we ensure we set the effecrt tag so that we visit
1862 - // this effect in the commit phase, so we can handle
1863 - // clean-up accordingly.
1864 - currentlyRenderingFiber.effectTag |= UpdateEffect;
1865 - pushEffect(NoHookEffect, noOpMount, clear, null);
1866 - hook.memoizedState = [reactListenerMap, event, clear];
1867 - return reactListenerMap;
1868 - }
1869 - // To make Flow not complain
1870 - return (undefined: any);
1871 -}
1872 -
1873 -function updateEventListener(event: ReactListenerEvent): ReactListenerMap {
1874 - if (enableUseEventAPI) {
1875 - const hook = updateWorkInProgressHook();
1876 - const [reactListenerMap, memoizedEvent, clear] = hook.memoizedState;
1877 - if (__DEV__) {
1878 - if (memoizedEvent.type !== event.type) {
1879 - console.warn(
1880 - 'The event type argument passed to the useEvent() hook was different between renders.' +
1881 - ' The event type is static and should never change between renders.',
1882 - );
1883 - }
1884 - if (memoizedEvent.capture !== event.capture) {
1885 - console.warn(
1886 - 'The "capture" option passed to the useEvent() hook was different between renders.' +
1887 - ' The "capture" option is static and should never change between renders.',
1888 - );
1889 - }
1890 - if (memoizedEvent.priority !== event.priority) {
1891 - console.warn(
1892 - 'The "priority" option passed to the useEvent() hook was different between renders.' +
1893 - ' The "priority" option is static and should never change between renders.',
1894 - );
1895 - }
1896 - if (memoizedEvent.passive !== event.passive) {
1897 - console.warn(
1898 - 'The "passive" option passed to the useEvent() hook was different between renders.' +
1899 - ' The "passive" option is static and should never change between renders.',
1900 - );
1901 - }
1902 - }
1903 - // In order to clear up upon the hook unmounting,
1904 - // we ensure we set the effecrt tag so that we visit
1905 - // this effect in the commit phase, so we can handle
1906 - // clean-up accordingly.
1907 - currentlyRenderingFiber.effectTag |= UpdateEffect;
1908 - pushEffect(NoHookEffect, noOpMount, clear, null);
1909 - return reactListenerMap;
1910 - }
1911 - // To make Flow not complain
1912 - return (undefined: any);
1913 -}
1914 -
1748 export const ContextOnlyDispatcher: Dispatcher = {
1749 readContext,
1750
@@ -1929,7 +1762,6 @@ export const ContextOnlyDispatcher: Dispatcher = {
1762 useDeferredValue: throwInvalidHookError,
1763 useTransition: throwInvalidHookError,
1764 useMutableSource: throwInvalidHookError,
1932 - useEvent: throwInvalidHookError,
1765 useOpaqueIdentifier: throwInvalidHookError,
1766 };
1767
@@ -1950,7 +1782,6 @@ const HooksDispatcherOnMount: Dispatcher = {
1782 useDeferredValue: mountDeferredValue,
1783 useTransition: mountTransition,
1784 useMutableSource: mountMutableSource,
1953 - useEvent: mountEventListener,
1785 useOpaqueIdentifier: mountOpaqueIdentifier,
1786 };
1787
@@ -1971,7 +1802,6 @@ const HooksDispatcherOnUpdate: Dispatcher = {
1802 useDeferredValue: updateDeferredValue,
1803 useTransition: updateTransition,
1804 useMutableSource: updateMutableSource,
1974 - useEvent: updateEventListener,
1805 useOpaqueIdentifier: updateOpaqueIdentifier,
1806 };
1807
@@ -1992,7 +1822,6 @@ const HooksDispatcherOnRerender: Dispatcher = {
1822 useDeferredValue: rerenderDeferredValue,
1823 useTransition: rerenderTransition,
1824 useMutableSource: updateMutableSource,
1995 - useEvent: updateEventListener,
1825 useOpaqueIdentifier: rerenderOpaqueIdentifier,
1826 };
1827
@@ -2151,11 +1980,6 @@ if (__DEV__) {
1980 mountHookTypesDev();
1981 return mountMutableSource(source, getSnapshot, subscribe);
1982 },
2154 - useEvent(event: ReactListenerEvent): ReactListenerMap {
2155 - currentHookNameInDev = 'useEvent';
2156 - mountHookTypesDev();
2157 - return mountEventListener(event);
2158 - },
1983 useOpaqueIdentifier(): OpaqueIDType | void {
1984 currentHookNameInDev = 'useOpaqueIdentifier';
1985 mountHookTypesDev();
@@ -2286,11 +2110,6 @@ if (__DEV__) {
2110 updateHookTypesDev();
2111 return mountMutableSource(source, getSnapshot, subscribe);
2112 },
2289 - useEvent(event: ReactListenerEvent): ReactListenerMap {
2290 - currentHookNameInDev = 'useEvent';
2291 - updateHookTypesDev();
2292 - return mountEventListener(event);
2293 - },
2113 useOpaqueIdentifier(): OpaqueIDType | void {
2114 currentHookNameInDev = 'useOpaqueIdentifier';
2115 updateHookTypesDev();
@@ -2421,11 +2240,6 @@ if (__DEV__) {
2240 updateHookTypesDev();
2241 return updateMutableSource(source, getSnapshot, subscribe);
2242 },
2424 - useEvent(event: ReactListenerEvent): ReactListenerMap {
2425 - currentHookNameInDev = 'useEvent';
2426 - updateHookTypesDev();
2427 - return updateEventListener(event);
2428 - },
2243 useOpaqueIdentifier(): OpaqueIDType | void {
2244 currentHookNameInDev = 'useOpaqueIdentifier';
2245 updateHookTypesDev();
@@ -2557,11 +2371,6 @@ if (__DEV__) {
2371 updateHookTypesDev();
2372 return updateMutableSource(source, getSnapshot, subscribe);
2373 },
2560 - useEvent(event: ReactListenerEvent): ReactListenerMap {
2561 - currentHookNameInDev = 'useEvent';
2562 - updateHookTypesDev();
2563 - return updateEventListener(event);
2564 - },
2374 useOpaqueIdentifier(): OpaqueIDType | void {
2375 currentHookNameInDev = 'useOpaqueIdentifier';
2376 updateHookTypesDev();
@@ -2707,12 +2516,6 @@ if (__DEV__) {
2516 mountHookTypesDev();
2517 return mountMutableSource(source, getSnapshot, subscribe);
2518 },
2710 - useEvent(event: ReactListenerEvent): ReactListenerMap {
2711 - currentHookNameInDev = 'useEvent';
2712 - warnInvalidHookAccess();
2713 - mountHookTypesDev();
2714 - return mountEventListener(event);
2715 - },
2519 useOpaqueIdentifier(): OpaqueIDType | void {
2520 currentHookNameInDev = 'useOpaqueIdentifier';
2521 warnInvalidHookAccess();
@@ -2859,12 +2662,6 @@ if (__DEV__) {
2662 updateHookTypesDev();
2663 return updateMutableSource(source, getSnapshot, subscribe);
2664 },
2862 - useEvent(event: ReactListenerEvent): ReactListenerMap {
2863 - currentHookNameInDev = 'useEvent';
2864 - warnInvalidHookAccess();
2865 - updateHookTypesDev();
2866 - return updateEventListener(event);
2867 - },
2665 useOpaqueIdentifier(): OpaqueIDType | void {
2666 currentHookNameInDev = 'useOpaqueIdentifier';
2667 warnInvalidHookAccess();
@@ -3012,12 +2809,6 @@ if (__DEV__) {
2809 updateHookTypesDev();
2810 return updateMutableSource(source, getSnapshot, subscribe);
2811 },
3015 - useEvent(event: ReactListenerEvent): ReactListenerMap {
3016 - currentHookNameInDev = 'useEvent';
3017 - warnInvalidHookAccess();
3018 - updateHookTypesDev();
3019 - return updateEventListener(event);
3020 - },
2812 useOpaqueIdentifier(): OpaqueIDType | void {
2813 currentHookNameInDev = 'useOpaqueIdentifier';
2814 warnInvalidHookAccess();
packages/react-reconciler/src/ReactFiberHooks.old.js
+2 -211
@@ -14,7 +14,6 @@ import type {
14 ReactEventResponder,
15 ReactContext,
16 ReactEventResponderListener,
17 - ReactScopeMethods,
17 } from 'shared/ReactTypes';
18 import type {Fiber, Dispatcher} from './ReactInternalTypes';
19 import type {ExpirationTime} from './ReactFiberExpirationTime.old';
@@ -22,15 +21,10 @@ import type {HookEffectTag} from './ReactHookEffectTags';
21 import type {SuspenseConfig} from './ReactFiberSuspenseConfig';
22 import type {ReactPriorityLevel} from './ReactInternalTypes';
23 import type {FiberRoot} from './ReactInternalTypes';
25 -import type {
26 - OpaqueIDType,
27 - ReactListenerEvent,
28 - ReactListenerMap,
29 - ReactListener,
30 -} from './ReactFiberHostConfig';
24 +import type {OpaqueIDType} from './ReactFiberHostConfig';
25
26 import ReactSharedInternals from 'shared/ReactSharedInternals';
33 -import {enableDebugTracing, enableUseEventAPI} from 'shared/ReactFeatureFlags';
27 +import {enableDebugTracing} from 'shared/ReactFeatureFlags';
28
29 import {markRootExpiredAtTime} from './ReactFiberRoot.old';
30 import {NoMode, BlockingMode, DebugTracingMode} from './ReactTypeOfMode';
@@ -46,7 +40,6 @@ import {
40 Passive as PassiveEffect,
41 } from './ReactSideEffectTags';
42 import {
49 - NoEffect as NoHookEffect,
43 HasEffect as HookHasEffect,
44 Layout as HookLayout,
45 Passive as HookPassive,
@@ -63,12 +56,6 @@ import {
56 markUnprocessedUpdateTime,
57 priorityLevelToLabel,
58 } from './ReactFiberWorkLoop.old';
66 -import {
67 - registerEvent,
68 - mountEventListener as mountHostEventListener,
69 - unmountEventListener as unmountHostEventListener,
70 - validateEventListenerTarget,
71 -} from './ReactFiberHostConfig';
59
60 import invariant from 'shared/invariant';
61 import getComponentName from 'shared/getComponentName';
@@ -95,7 +82,6 @@ import {
82 setWorkInProgressVersion,
83 warnAboutMultipleRenderersDEV,
84 } from './ReactMutableSource.old';
98 -import {getRootHostContainer} from './ReactFiberHostContext.old';
85 import {getIsRendering} from './ReactCurrentFiber';
86 import {logStateUpdateScheduled} from './DebugTracing';
87
@@ -133,7 +119,6 @@ export type HookType =
119 | 'useDeferredValue'
120 | 'useTransition'
121 | 'useMutableSource'
136 - | 'useEvent'
122 | 'useOpaqueIdentifier';
123
124 let didWarnAboutMismatchedHooksForComponent;
@@ -1755,158 +1740,6 @@ function dispatchAction<S, A>(
1740 }
1741 }
1742
1758 -const noOpMount = () => {};
1759 -
1760 -function validateNotInFunctionRender(): boolean {
1761 - if (currentlyRenderingFiber === null) {
1762 - return true;
1763 - }
1764 - if (__DEV__) {
1765 - console.warn(
1766 - 'Event listener methods from useEvent() cannot be called during render.' +
1767 - ' These methods should be called in an effect or event callback outside the render.',
1768 - );
1769 - }
1770 - return false;
1771 -}
1772 -
1773 -function createReactListener(
1774 - event: ReactListenerEvent,
1775 - callback: (SyntheticEvent<EventTarget>) => void,
1776 - target: EventTarget | ReactScopeMethods,
1777 - destroy: Node => void,
1778 -): ReactListener {
1779 - return {
1780 - callback,
1781 - destroy,
1782 - event,
1783 - target,
1784 - };
1785 -}
1786 -
1787 -function mountEventListener(event: ReactListenerEvent): ReactListenerMap {
1788 - if (enableUseEventAPI) {
1789 - const hook = mountWorkInProgressHook();
1790 - const listenerMap: Map<
1791 - EventTarget | ReactScopeMethods,
1792 - ReactListener,
1793 - > = new Map();
1794 - const rootContainerInstance = getRootHostContainer();
1795 -
1796 - // Register the event to the current root to ensure event
1797 - // replaying can pick up the event ahead of time.
1798 - registerEvent(event, rootContainerInstance);
1799 -
1800 - const clear = () => {
1801 - if (validateNotInFunctionRender()) {
1802 - const listeners = Array.from(listenerMap.values());
1803 - for (let i = 0; i < listeners.length; i++) {
1804 - unmountHostEventListener(listeners[i]);
1805 - }
1806 - listenerMap.clear();
1807 - }
1808 - };
1809 -
1810 - const destroy = (target: Node) => {
1811 - // We don't need to call detachListenerFromInstance
1812 - // here as this method should only ever be called
1813 - // from renderers that need to remove the instance
1814 - // from the map representing an instance that still
1815 - // holds a reference to the listenerMap. This means
1816 - // things like "window" listeners on ReactDOM should
1817 - // never enter this call path as the the instance in
1818 - // those cases would be that of "window", which
1819 - // should be handled via an optimized route in the
1820 - // renderer, making less overhead here. If we change
1821 - // this heuristic we should update this path to make
1822 - // sure we call detachListenerFromInstance.
1823 - listenerMap.delete(target);
1824 - };
1825 -
1826 - const reactListenerMap: ReactListenerMap = {
1827 - clear,
1828 - setListener(
1829 - target: EventTarget | ReactScopeMethods,
1830 - callback: ?(SyntheticEvent<EventTarget>) => void,
1831 - ): void {
1832 - if (
1833 - validateNotInFunctionRender() &&
1834 - validateEventListenerTarget(target, callback)
1835 - ) {
1836 - let listener = listenerMap.get(target);
1837 - if (listener === undefined) {
1838 - if (callback == null) {
1839 - return;
1840 - }
1841 - listener = createReactListener(event, callback, target, destroy);
1842 - listenerMap.set(target, listener);
1843 - } else {
1844 - if (callback == null) {
1845 - listenerMap.delete(target);
1846 - unmountHostEventListener(listener);
1847 - return;
1848 - }
1849 - listener.callback = callback;
1850 - }
1851 - mountHostEventListener(listener);
1852 - }
1853 - },
1854 - };
1855 - // In order to clear up upon the hook unmounting,
1856 - // we ensure we set the effecrt tag so that we visit
1857 - // this effect in the commit phase, so we can handle
1858 - // clean-up accordingly.
1859 - currentlyRenderingFiber.effectTag |= UpdateEffect;
1860 - pushEffect(NoHookEffect, noOpMount, clear, null);
1861 - hook.memoizedState = [reactListenerMap, event, clear];
1862 - return reactListenerMap;
1863 - }
1864 - // To make Flow not complain
1865 - return (undefined: any);
1866 -}
1867 -
1868 -function updateEventListener(event: ReactListenerEvent): ReactListenerMap {
1869 - if (enableUseEventAPI) {
1870 - const hook = updateWorkInProgressHook();
1871 - const [reactListenerMap, memoizedEvent, clear] = hook.memoizedState;
1872 - if (__DEV__) {
1873 - if (memoizedEvent.type !== event.type) {
1874 - console.warn(
1875 - 'The event type argument passed to the useEvent() hook was different between renders.' +
1876 - ' The event type is static and should never change between renders.',
1877 - );
1878 - }
1879 - if (memoizedEvent.capture !== event.capture) {
1880 - console.warn(
1881 - 'The "capture" option passed to the useEvent() hook was different between renders.' +
1882 - ' The "capture" option is static and should never change between renders.',
1883 - );
1884 - }
1885 - if (memoizedEvent.priority !== event.priority) {
1886 - console.warn(
1887 - 'The "priority" option passed to the useEvent() hook was different between renders.' +
1888 - ' The "priority" option is static and should never change between renders.',
1889 - );
1890 - }
1891 - if (memoizedEvent.passive !== event.passive) {
1892 - console.warn(
1893 - 'The "passive" option passed to the useEvent() hook was different between renders.' +
1894 - ' The "passive" option is static and should never change between renders.',
1895 - );
1896 - }
1897 - }
1898 - // In order to clear up upon the hook unmounting,
1899 - // we ensure we set the effecrt tag so that we visit
1900 - // this effect in the commit phase, so we can handle
1901 - // clean-up accordingly.
1902 - currentlyRenderingFiber.effectTag |= UpdateEffect;
1903 - pushEffect(NoHookEffect, noOpMount, clear, null);
1904 - return reactListenerMap;
1905 - }
1906 - // To make Flow not complain
1907 - return (undefined: any);
1908 -}
1909 -
1743 export const ContextOnlyDispatcher: Dispatcher = {
1744 readContext,
1745
@@ -1924,7 +1757,6 @@ export const ContextOnlyDispatcher: Dispatcher = {
1757 useDeferredValue: throwInvalidHookError,
1758 useTransition: throwInvalidHookError,
1759 useMutableSource: throwInvalidHookError,
1927 - useEvent: throwInvalidHookError,
1760 useOpaqueIdentifier: throwInvalidHookError,
1761 };
1762
@@ -1945,7 +1777,6 @@ const HooksDispatcherOnMount: Dispatcher = {
1777 useDeferredValue: mountDeferredValue,
1778 useTransition: mountTransition,
1779 useMutableSource: mountMutableSource,
1948 - useEvent: mountEventListener,
1780 useOpaqueIdentifier: mountOpaqueIdentifier,
1781 };
1782
@@ -1966,7 +1797,6 @@ const HooksDispatcherOnUpdate: Dispatcher = {
1797 useDeferredValue: updateDeferredValue,
1798 useTransition: updateTransition,
1799 useMutableSource: updateMutableSource,
1969 - useEvent: updateEventListener,
1800 useOpaqueIdentifier: updateOpaqueIdentifier,
1801 };
1802
@@ -1987,7 +1817,6 @@ const HooksDispatcherOnRerender: Dispatcher = {
1817 useDeferredValue: rerenderDeferredValue,
1818 useTransition: rerenderTransition,
1819 useMutableSource: updateMutableSource,
1990 - useEvent: updateEventListener,
1820 useOpaqueIdentifier: rerenderOpaqueIdentifier,
1821 };
1822
@@ -2146,11 +1975,6 @@ if (__DEV__) {
1975 mountHookTypesDev();
1976 return mountMutableSource(source, getSnapshot, subscribe);
1977 },
2149 - useEvent(event: ReactListenerEvent): ReactListenerMap {
2150 - currentHookNameInDev = 'useEvent';
2151 - mountHookTypesDev();
2152 - return mountEventListener(event);
2153 - },
1978 useOpaqueIdentifier(): OpaqueIDType | void {
1979 currentHookNameInDev = 'useOpaqueIdentifier';
1980 mountHookTypesDev();
@@ -2281,11 +2105,6 @@ if (__DEV__) {
2105 updateHookTypesDev();
2106 return mountMutableSource(source, getSnapshot, subscribe);
2107 },
2284 - useEvent(event: ReactListenerEvent): ReactListenerMap {
2285 - currentHookNameInDev = 'useEvent';
2286 - updateHookTypesDev();
2287 - return mountEventListener(event);
2288 - },
2108 useOpaqueIdentifier(): OpaqueIDType | void {
2109 currentHookNameInDev = 'useOpaqueIdentifier';
2110 updateHookTypesDev();
@@ -2416,11 +2235,6 @@ if (__DEV__) {
2235 updateHookTypesDev();
2236 return updateMutableSource(source, getSnapshot, subscribe);
2237 },
2419 - useEvent(event: ReactListenerEvent): ReactListenerMap {
2420 - currentHookNameInDev = 'useEvent';
2421 - updateHookTypesDev();
2422 - return updateEventListener(event);
2423 - },
2238 useOpaqueIdentifier(): OpaqueIDType | void {
2239 currentHookNameInDev = 'useOpaqueIdentifier';
2240 updateHookTypesDev();
@@ -2552,11 +2366,6 @@ if (__DEV__) {
2366 updateHookTypesDev();
2367 return updateMutableSource(source, getSnapshot, subscribe);
2368 },
2555 - useEvent(event: ReactListenerEvent): ReactListenerMap {
2556 - currentHookNameInDev = 'useEvent';
2557 - updateHookTypesDev();
2558 - return updateEventListener(event);
2559 - },
2369 useOpaqueIdentifier(): OpaqueIDType | void {
2370 currentHookNameInDev = 'useOpaqueIdentifier';
2371 updateHookTypesDev();
@@ -2702,12 +2511,6 @@ if (__DEV__) {
2511 mountHookTypesDev();
2512 return mountMutableSource(source, getSnapshot, subscribe);
2513 },
2705 - useEvent(event: ReactListenerEvent): ReactListenerMap {
2706 - currentHookNameInDev = 'useEvent';
2707 - warnInvalidHookAccess();
2708 - mountHookTypesDev();
2709 - return mountEventListener(event);
2710 - },
2514 useOpaqueIdentifier(): OpaqueIDType | void {
2515 currentHookNameInDev = 'useOpaqueIdentifier';
2516 warnInvalidHookAccess();
@@ -2854,12 +2657,6 @@ if (__DEV__) {
2657 updateHookTypesDev();
2658 return updateMutableSource(source, getSnapshot, subscribe);
2659 },
2857 - useEvent(event: ReactListenerEvent): ReactListenerMap {
2858 - currentHookNameInDev = 'useEvent';
2859 - warnInvalidHookAccess();
2860 - updateHookTypesDev();
2861 - return updateEventListener(event);
2862 - },
2660 useOpaqueIdentifier(): OpaqueIDType | void {
2661 currentHookNameInDev = 'useOpaqueIdentifier';
2662 warnInvalidHookAccess();
@@ -3007,12 +2804,6 @@ if (__DEV__) {
2804 updateHookTypesDev();
2805 return updateMutableSource(source, getSnapshot, subscribe);
2806 },
3010 - useEvent(event: ReactListenerEvent): ReactListenerMap {
3011 - currentHookNameInDev = 'useEvent';
3012 - warnInvalidHookAccess();
3013 - updateHookTypesDev();
3014 - return updateEventListener(event);
3015 - },
2807 useOpaqueIdentifier(): OpaqueIDType | void {
2808 currentHookNameInDev = 'useOpaqueIdentifier';
2809 warnInvalidHookAccess();
packages/react-reconciler/src/ReactInternalTypes.js
+2 -9
@@ -18,10 +18,7 @@ import type {
18 MutableSourceGetSnapshotFn,
19 MutableSource,
20 } from 'shared/ReactTypes';
21 -import type {
22 - SuspenseInstance,
23 - ReactListenerEvent,
24 -} from './ReactFiberHostConfig';
21 +import type {SuspenseInstance} from './ReactFiberHostConfig';
22 import type {WorkTag} from './ReactWorkTags';
23 import type {TypeOfMode} from './ReactTypeOfMode';
24 import type {SideEffectTag} from './ReactSideEffectTags';
@@ -32,10 +29,7 @@ import type {RootTag} from './ReactRootTags';
29 import type {TimeoutHandle, NoTimeout} from './ReactFiberHostConfig';
30 import type {Wakeable} from 'shared/ReactTypes';
31 import type {Interaction} from 'scheduler/src/Tracing';
35 -import type {
36 - OpaqueIDType,
37 - ReactListenerMap,
38 -} from 'react-reconciler/src/ReactFiberHostConfig';
32 +import type {OpaqueIDType} from 'react-reconciler/src/ReactFiberHostConfig';
33 import type {SuspenseConfig, TimeoutConfig} from './ReactFiberSuspenseConfig';
34
35 export type ReactPriorityLevel = 99 | 98 | 97 | 96 | 95 | 90;
@@ -325,6 +319,5 @@ export type Dispatcher = {|
319 getSnapshot: MutableSourceGetSnapshotFn<Source, Snapshot>,
320 subscribe: MutableSourceSubscribeFn<Source, Snapshot>,
321 ): Snapshot,
328 - useEvent(event: ReactListenerEvent): ReactListenerMap,
322 useOpaqueIdentifier(): OpaqueIDType | void,
323 |};
packages/react-reconciler/src/forks/ReactFiberHostConfig.custom.js
-8
@@ -40,9 +40,6 @@ export opaque type NoTimeout = mixed; // eslint-disable-line no-undef
40 export opaque type RendererInspectionConfig = mixed; // eslint-disable-line no-undef
41 export opaque type OpaqueIDType = mixed;
42 export type EventResponder = any;
43 -export type ReactListenerEvent = Object;
44 -export type ReactListenerMap = Object;
45 -export type ReactListener = Object;
43
44 export const getPublicInstance = $$$hostConfig.getPublicInstance;
45 export const getRootHostContext = $$$hostConfig.getRootHostContext;
@@ -78,11 +75,6 @@ export const shouldUpdateFundamentalComponent =
75 $$$hostConfig.shouldUpdateFundamentalComponent;
76 export const getInstanceFromNode = $$$hostConfig.getInstanceFromNode;
77 export const beforeRemoveInstance = $$$hostConfig.beforeRemoveInstance;
81 -export const registerEvent = $$$hostConfig.registerEvent;
82 -export const mountEventListener = $$$hostConfig.mountEventListener;
83 -export const unmountEventListener = $$$hostConfig.unmountEventListener;
84 -export const validateEventListenerTarget =
85 - $$$hostConfig.validateEventListenerTarget;
78 export const isOpaqueHydratingObject = $$$hostConfig.isOpaqueHydratingObject;
79 export const makeOpaqueHydratingObject =
80 $$$hostConfig.makeOpaqueHydratingObject;
packages/react-test-renderer/src/ReactTestHostConfig.js
-19
@@ -52,9 +52,6 @@ export opaque type OpaqueIDType =
52 valueOf: () => string | void,
53 };
54
55 -export type ReactListenerEvent = Object;
56 -export type ReactListenerMap = Object;
57 -export type ReactListener = Object;
55 export type RendererInspectionConfig = $ReadOnly<{||}>;
56
57 export * from 'react-reconciler/src/ReactFiberHostConfigWithNoPersistence';
@@ -431,22 +428,6 @@ export function makeOpaqueHydratingObject(
428 };
429 }
430
434 -export function registerEvent(event: any, rootContainerInstance: Container) {
435 - throw new Error('Not yet implemented.');
436 -}
437 -
438 -export function mountEventListener(listener: any) {
439 - throw new Error('Not yet implemented.');
440 -}
441 -
442 -export function unmountEventListener(listener: any) {
443 - throw new Error('Not yet implemented.');
444 -}
445 -
446 -export function validateEventListenerTarget(target: any, listener: any) {
447 - throw new Error('Not yet implemented.');
448 -}
449 -
431 export function beforeActiveInstanceBlur() {
432 // noop
433 }
packages/shared/ReactFeatureFlags.js
-3
@@ -57,9 +57,6 @@ export const enableFundamentalAPI = false;
57 // Experimental Scope support.
58 export const enableScopeAPI = false;
59
60 -// Experimental useEvent support.
61 -export const enableUseEventAPI = false;
62 -
60 // New API for JSX transforms to target - https://github.com/reactjs/rfcs/pull/107
61
62 // We will enforce mocking scheduler with scheduler/unstable_mock at some point. (v17?)
packages/shared/forks/ReactFeatureFlags.native-fb.js
-1
@@ -27,7 +27,6 @@ export const warnAboutDeprecatedLifecycles = true;
27 export const enableDeprecatedFlareAPI = false;
28 export const enableFundamentalAPI = false;
29 export const enableScopeAPI = false;
30 -export const enableUseEventAPI = false;
30 export const warnAboutUnmockedScheduler = true;
31 export const enableSuspenseCallback = false;
32 export const warnAboutDefaultPropsOnFunctionComponents = false;
packages/shared/forks/ReactFeatureFlags.native-oss.js
-1
@@ -26,7 +26,6 @@ export const enableSchedulerDebugging = false;
26 export const enableDeprecatedFlareAPI = false;
27 export const enableFundamentalAPI = false;
28 export const enableScopeAPI = false;
29 -export const enableUseEventAPI = false;
29 export const warnAboutUnmockedScheduler = false;
30 export const enableSuspenseCallback = false;
31 export const warnAboutDefaultPropsOnFunctionComponents = false;
packages/shared/forks/ReactFeatureFlags.test-renderer.js
-1
@@ -26,7 +26,6 @@ export const enableSchedulerDebugging = false;
26 export const enableDeprecatedFlareAPI = false;
27 export const enableFundamentalAPI = false;
28 export const enableScopeAPI = false;
29 -export const enableUseEventAPI = false;
29 export const warnAboutUnmockedScheduler = false;
30 export const enableSuspenseCallback = false;
31 export const warnAboutDefaultPropsOnFunctionComponents = false;
packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
-1
@@ -26,7 +26,6 @@ export const disableInputAttributeSyncing = false;
26 export const enableDeprecatedFlareAPI = true;
27 export const enableFundamentalAPI = false;
28 export const enableScopeAPI = true;
29 -export const enableUseEventAPI = true;
29 export const warnAboutUnmockedScheduler = true;
30 export const enableSuspenseCallback = true;
31 export const warnAboutDefaultPropsOnFunctionComponents = false;
packages/shared/forks/ReactFeatureFlags.testing.js
-1
@@ -26,7 +26,6 @@ export const enableSchedulerDebugging = false;
26 export const enableDeprecatedFlareAPI = false;
27 export const enableFundamentalAPI = false;
28 export const enableScopeAPI = false;
29 -export const enableUseEventAPI = false;
29 export const warnAboutUnmockedScheduler = false;
30 export const enableSuspenseCallback = false;
31 export const warnAboutDefaultPropsOnFunctionComponents = false;
packages/shared/forks/ReactFeatureFlags.testing.www.js
-1
@@ -26,7 +26,6 @@ export const enableSchedulerDebugging = false;
26 export const enableDeprecatedFlareAPI = true;
27 export const enableFundamentalAPI = false;
28 export const enableScopeAPI = true;
29 -export const enableUseEventAPI = true;
29 export const warnAboutUnmockedScheduler = true;
30 export const enableSuspenseCallback = true;
31 export const warnAboutDefaultPropsOnFunctionComponents = false;
packages/shared/forks/ReactFeatureFlags.www.js
-2
@@ -60,8 +60,6 @@ export const enableFundamentalAPI = false;
60
61 export const enableScopeAPI = true;
62
63 -export const enableUseEventAPI = true;
64 -
63 export const warnAboutUnmockedScheduler = true;
64
65 export const enableSuspenseCallback = true;