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

This commit is contained in:
Dominic Gannaway
2020-04-21 16:40:44 +01:00
committed by GitHub
parent 7b3934b34f
commit ff431b7fc4
37 changed files with 31 additions and 4152 deletions
-16
View File
@@ -493,22 +493,6 @@ export function makeServerId(): OpaqueIDType {
throw new Error('Not yet implemented');
}
export function registerEvent(event: any, rootContainerInstance: any) {
throw new Error('Not yet implemented.');
}
export function mountEventListener(listener: any) {
throw new Error('Not yet implemented.');
}
export function unmountEventListener(listener: any) {
throw new Error('Not yet implemented.');
}
export function validateEventListenerTarget(target: any, listener: any) {
throw new Error('Not yet implemented.');
}
export function beforeActiveInstanceBlur() {
// noop
}
-20
View File
@@ -15,7 +15,6 @@ import type {
ReactProviderType,
ReactEventResponder,
ReactEventResponderListener,
ReactScopeMethods,
} from 'shared/ReactTypes';
import type {
Fiber,
@@ -48,14 +47,6 @@ type HookLogEntry = {
...
};
type ReactDebugListenerMap = {|
clear: () => void,
setListener: (
target: EventTarget | ReactScopeMethods,
callback: ?(SyntheticEvent<EventTarget>) => void,
) => void,
|};
let hookLog: Array<HookLogEntry> = [];
// Primitives
@@ -311,16 +302,6 @@ function useTransition(
return [callback => {}, false];
}
const noOp = () => {};
function useEvent(event: any): ReactDebugListenerMap {
hookLog.push({primitive: 'Event', stackError: new Error(), value: event});
return {
clear: noOp,
setListener: noOp,
};
}
function useDeferredValue<T>(value: T, config: TimeoutConfig | null | void): T {
// useDeferredValue() composes multiple hooks internally.
// Advance the current hook index the same number of times
@@ -368,7 +349,6 @@ const Dispatcher: DispatcherType = {
useTransition,
useMutableSource,
useDeferredValue,
useEvent,
useOpaqueIdentifier,
};
@@ -11,7 +11,6 @@
'use strict';
let React;
let ReactDOM;
let ReactDebugTools;
describe('ReactHooksInspection', () => {
@@ -19,9 +18,7 @@ describe('ReactHooksInspection', () => {
jest.resetModules();
const ReactFeatureFlags = require('shared/ReactFeatureFlags');
ReactFeatureFlags.enableDeprecatedFlareAPI = true;
ReactFeatureFlags.enableUseEventAPI = true;
React = require('react');
ReactDOM = require('react-dom');
ReactDebugTools = require('react-debug-tools');
});
@@ -46,45 +43,4 @@ describe('ReactHooksInspection', () => {
},
]);
});
// @gate experimental
it('should inspect a simple ReactDOM.useEvent hook', () => {
let clickHandle;
let ref;
const effect = () => {
clickHandle.setListener(ref.current, () => {});
};
function Foo(props) {
ref = React.useRef(null);
clickHandle = ReactDOM.unstable_useEvent('click');
React.useEffect(effect);
return <div ref={ref}>Hello world</div>;
}
const tree = ReactDebugTools.inspectHooks(Foo, {});
expect(tree).toEqual([
{
isStateEditable: false,
id: 0,
name: 'Ref',
subHooks: [],
value: null,
},
{
isStateEditable: false,
id: 1,
name: 'Event',
value: {capture: false, passive: undefined, priority: 0, type: 'click'},
subHooks: [],
},
{
isStateEditable: false,
id: 2,
name: 'Effect',
value: effect,
subHooks: [],
},
]);
});
});
-1
View File
@@ -36,5 +36,4 @@ export {
unstable_scheduleHydration,
unstable_renderSubtreeIntoContainer,
unstable_createPortal,
unstable_useEvent,
} from './src/client/ReactDOM';
-1
View File
@@ -19,5 +19,4 @@ export {
unstable_flushDiscreteUpdates,
unstable_flushControlled,
unstable_scheduleHydration,
unstable_useEvent,
} from './src/client/ReactDOM';
-3
View File
@@ -20,7 +20,6 @@ import {
unmountComponentAtNode,
} from './ReactDOMLegacy';
import {createRoot, createBlockingRoot, isValidContainer} from './ReactDOMRoot';
import {useEvent} from './ReactDOMUseEvent';
import {
batchedEventUpdates,
@@ -211,8 +210,6 @@ export {
// Temporary alias since we already shipped React 16 RC with it.
// TODO: remove in React 17.
unstable_createPortal,
// enableUseEventAPI
useEvent as unstable_useEvent,
};
const foundDevTools = injectIntoDevTools({
-17
View File
@@ -15,7 +15,6 @@ import type {
SuspenseInstance,
Props,
} from './ReactDOMHostConfig';
import type {ReactDOMListener} from '../shared/ReactDOMTypes';
import {
HostComponent,
@@ -33,7 +32,6 @@ const randomKey = Math.random()
const internalInstanceKey = '__reactFiber$' + randomKey;
const internalEventHandlersKey = '__reactEvents$' + randomKey;
const internalContainerInstanceKey = '__reactContainer$' + randomKey;
const internalEventListenersKey = '__reactListeners$' + randomKey;
export function precacheFiberNode(
hostInst: Fiber,
@@ -187,18 +185,3 @@ export function updateFiberProps(
): void {
(node: any)[internalEventHandlersKey] = props;
}
// This is used for useEvent listeners
export function getListenersFromTarget(
target: EventTarget,
): null | Set<ReactDOMListener> {
return (target: any)[internalEventListenersKey] || null;
}
// This is used for useEvent listeners
export function initListenersSet(
target: EventTarget,
value: Set<ReactDOMListener>,
): void {
(target: any)[internalEventListenersKey] = value;
}
+7 -125
View File
@@ -13,17 +13,12 @@ import type {
ReactDOMEventResponder,
ReactDOMEventResponderInstance,
ReactDOMFundamentalComponentInstance,
ReactDOMListener,
ReactDOMListenerEvent,
ReactDOMListenerMap,
} from '../shared/ReactDOMTypes';
import type {ReactScopeMethods} from 'shared/ReactTypes';
import {
precacheFiberNode,
updateFiberProps,
getClosestInstanceFromNode,
getListenersFromTarget,
} from './ReactDOMComponentTree';
import {
createElement,
@@ -69,32 +64,9 @@ import {
enableSuspenseServerRenderer,
enableDeprecatedFlareAPI,
enableFundamentalAPI,
enableUseEventAPI,
enableScopeAPI,
} from 'shared/ReactFeatureFlags';
import {
PLUGIN_EVENT_SYSTEM,
USE_EVENT_SYSTEM,
} from '../events/EventSystemFlags';
import {
isManagedDOMElement,
isValidEventTarget,
listenToTopLevelEvent,
attachListenerToManagedDOMElement,
detachListenerFromManagedDOMElement,
attachTargetEventListener,
detachTargetEventListener,
isReactScope,
attachListenerToReactScope,
detachListenerFromReactScope,
} from '../events/DOMModernPluginEventSystem';
import {getListenerMapForElement} from '../events/DOMEventListenerMap';
import {TOP_BEFORE_BLUR, TOP_AFTER_BLUR} from '../events/DOMTopLevelEventTypes';
export type ReactListenerEvent = ReactDOMListenerEvent;
export type ReactListenerMap = ReactDOMListenerMap;
export type ReactListener = ReactDOMListener;
export type Type = string;
export type Props = {
autoFocus?: boolean,
@@ -246,7 +218,7 @@ export function prepareForCommit(containerInfo: Container): Object | null {
eventsEnabled = ReactBrowserEventEmitterIsEnabled();
selectionInformation = getSelectionInformation();
let activeInstance = null;
if (enableDeprecatedFlareAPI || enableUseEventAPI) {
if (enableDeprecatedFlareAPI) {
const focusedElem = selectionInformation.focusedElem;
if (focusedElem !== null) {
activeInstance = getClosestInstanceFromNode(focusedElem);
@@ -257,7 +229,7 @@ export function prepareForCommit(containerInfo: Container): Object | null {
}
export function beforeActiveInstanceBlur(): void {
if (enableDeprecatedFlareAPI || enableUseEventAPI) {
if (enableDeprecatedFlareAPI) {
ReactBrowserEventEmitterSetEnabled(true);
dispatchBeforeDetachedBlur((selectionInformation: any).focusedElem);
ReactBrowserEventEmitterSetEnabled(false);
@@ -265,7 +237,7 @@ export function beforeActiveInstanceBlur(): void {
}
export function afterActiveInstanceBlur(): void {
if (enableDeprecatedFlareAPI || enableUseEventAPI) {
if (enableDeprecatedFlareAPI) {
ReactBrowserEventEmitterSetEnabled(true);
dispatchAfterDetachedBlur((selectionInformation: any).focusedElem);
ReactBrowserEventEmitterSetEnabled(false);
@@ -528,7 +500,7 @@ function createEvent(type: TopLevelType): Event {
}
function dispatchBeforeDetachedBlur(target: HTMLElement): void {
if (enableDeprecatedFlareAPI || enableUseEventAPI) {
if (enableDeprecatedFlareAPI) {
const event = createEvent(TOP_BEFORE_BLUR);
// Dispatch "beforeblur" directly on the target,
// so it gets picked up by the event system and
@@ -538,7 +510,7 @@ function dispatchBeforeDetachedBlur(target: HTMLElement): void {
}
function dispatchAfterDetachedBlur(target: HTMLElement): void {
if (enableDeprecatedFlareAPI || enableUseEventAPI) {
if (enableDeprecatedFlareAPI) {
const event = createEvent(TOP_AFTER_BLUR);
// So we know what was detached, make the relatedTarget the
// detached target on the "afterblur" event.
@@ -550,23 +522,8 @@ function dispatchAfterDetachedBlur(target: HTMLElement): void {
export function beforeRemoveInstance(
instance: Instance | TextInstance | SuspenseInstance,
): void {
if (enableUseEventAPI) {
// It's unfortunate that we have to do this cleanup, but
// it's necessary otherwise we will leak the host instances
// from the useEvent hook instances Map. We call destroy
// on each listener to ensure we properly remove the instance
// from the instances Map. Note: we have this Map so that we
// can properly unmount instances when the function component
// that the hook is attached to gets unmounted.
const listenersSet = getListenersFromTarget(instance);
if (listenersSet !== null) {
const listeners = Array.from(listenersSet);
for (let i = 0; i < listeners.length; i++) {
listeners[i].destroy(instance);
}
}
}
) {
// TODO for ReactDOM.createEventInstance
}
export function removeChild(
@@ -1141,78 +1098,3 @@ export function makeOpaqueHydratingObject(
valueOf: attemptToReadValue,
};
}
export function registerEvent(
event: ReactDOMListenerEvent,
rootContainerInstance: Container,
): void {
const {passive, priority, type} = event;
const listenerMap = getListenerMapForElement(rootContainerInstance);
// Add the event listener to the target container (falling back to
// the target if we didn't find one).
listenToTopLevelEvent(
type,
rootContainerInstance,
listenerMap,
PLUGIN_EVENT_SYSTEM | USE_EVENT_SYSTEM,
passive,
priority,
);
}
export function mountEventListener(listener: ReactDOMListener): void {
if (enableUseEventAPI) {
const {target} = listener;
if (isManagedDOMElement(target)) {
attachListenerToManagedDOMElement(listener);
} else if (enableScopeAPI && isReactScope(target)) {
attachListenerToReactScope(listener);
} else {
attachTargetEventListener(listener);
}
}
}
export function unmountEventListener(listener: ReactDOMListener): void {
if (enableUseEventAPI) {
const {target} = listener;
if (isManagedDOMElement(target)) {
detachListenerFromManagedDOMElement(listener);
} else if (enableScopeAPI && isReactScope(target)) {
detachListenerFromReactScope(listener);
} else {
detachTargetEventListener(listener);
}
}
}
export function validateEventListenerTarget(
target: EventTarget | ReactScopeMethods,
listener: ?(SyntheticEvent<EventTarget>) => void,
): boolean {
if (enableUseEventAPI) {
if (
target != null &&
(isManagedDOMElement(target) ||
isValidEventTarget(target) ||
isReactScope(target))
) {
if (listener == null || typeof listener === 'function') {
return true;
}
if (__DEV__) {
console.warn(
'Event listener method setListener() from useEvent() hook requires the second argument' +
' to be either a valid function callback or null/undefined.',
);
}
}
if (__DEV__) {
console.warn(
'Event listener method setListener() from useEvent() hook requires the first argument to be ' +
'a valid DOM EventTarget. If using a ref, ensure the current value is not null.',
);
}
}
return false;
}
-79
View File
@@ -1,79 +0,0 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
import type {DOMTopLevelEventType} from 'legacy-events/TopLevelEventTypes';
import type {EventPriority} from 'shared/ReactTypes';
import type {
ReactDOMListenerEvent,
ReactDOMListenerMap,
} from '../shared/ReactDOMTypes';
import ReactSharedInternals from 'shared/ReactSharedInternals';
import invariant from 'shared/invariant';
import {getEventPriorityForListenerSystem} from '../events/DOMEventProperties';
type EventOptions = {|
capture?: boolean,
passive?: boolean,
priority?: EventPriority,
|};
const {ReactCurrentDispatcher} = ReactSharedInternals;
function resolveDispatcher() {
const dispatcher = ReactCurrentDispatcher.current;
invariant(
dispatcher !== null,
'Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for' +
' one of the following reasons:\n' +
'1. You might have mismatching versions of React and the renderer (such as React DOM)\n' +
'2. You might be breaking the Rules of Hooks\n' +
'3. You might have more than one copy of React in the same app\n' +
'See https://fb.me/react-invalid-hook-call for tips about how to debug and fix this problem.',
);
return dispatcher;
}
export function useEvent(
type: string,
options?: EventOptions,
): ReactDOMListenerMap {
const topLevelType = ((type: any): DOMTopLevelEventType);
const dispatcher = resolveDispatcher();
let capture = false;
let passive = undefined; // Undefined means to use the browser default
let priority;
if (options != null) {
const optionsCapture = options.capture;
const optionsPassive = options.passive;
const optionsPriority = options.priority;
if (typeof optionsCapture === 'boolean') {
capture = optionsCapture;
}
if (typeof optionsPassive === 'boolean') {
passive = optionsPassive;
}
if (typeof optionsPriority === 'number') {
priority = optionsPriority;
}
}
if (priority === undefined) {
priority = getEventPriorityForListenerSystem(topLevelType);
}
const event: ReactDOMListenerEvent = {
capture,
passive,
priority,
type: topLevelType,
};
return dispatcher.useEvent(event);
}
-8
View File
@@ -23,7 +23,6 @@ import {
UserBlockingEvent,
ContinuousEvent,
} from 'shared/ReactTypes';
import {enableUseEventAPI} from 'shared/ReactFeatureFlags';
// Needed for SimpleEventPlugin, rather than
// do it in two places, which duplicates logic
@@ -96,13 +95,6 @@ const otherDiscreteEvents = [
DOMTopLevelEventTypes.TOP_COMPOSITION_UPDATE,
];
if (enableUseEventAPI) {
otherDiscreteEvents.push(
DOMTopLevelEventTypes.TOP_BEFORE_BLUR,
DOMTopLevelEventTypes.TOP_AFTER_BLUR,
);
}
// prettier-ignore
const userBlockingPairsForSimpleEventPlugin = [
DOMTopLevelEventTypes.TOP_DRAG, 'drag',
+4 -347
View File
@@ -17,11 +17,7 @@ import type {EventSystemFlags} from './EventSystemFlags';
import type {EventPriority, ReactScopeMethods} from 'shared/ReactTypes';
import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
import type {PluginModule} from 'legacy-events/PluginModuleType';
import type {
ReactSyntheticEvent,
CustomDispatchConfig,
} from 'legacy-events/ReactSyntheticEventType';
import type {ReactDOMListener} from '../shared/ReactDOMTypes';
import type {ReactSyntheticEvent} from 'legacy-events/ReactSyntheticEventType';
import {registrationNameDependencies} from 'legacy-events/EventPluginRegistry';
import {plugins} from 'legacy-events/EventPluginRegistry';
@@ -36,7 +32,6 @@ import {
import {
HostRoot,
HostPortal,
ScopeComponent,
HostComponent,
} from 'react-reconciler/src/ReactWorkTags';
@@ -80,25 +75,14 @@ import {
TOP_PROGRESS,
TOP_PLAYING,
TOP_CLICK,
TOP_BEFORE_BLUR,
TOP_AFTER_BLUR,
TOP_SELECTION_CHANGE,
} from './DOMTopLevelEventTypes';
import {
getClosestInstanceFromNode,
getListenersFromTarget,
initListenersSet,
} from '../client/ReactDOMComponentTree';
import {getClosestInstanceFromNode} from '../client/ReactDOMComponentTree';
import {COMMENT_NODE} from '../shared/HTMLNodeType';
import {topLevelEventsToDispatchConfig} from './DOMEventProperties';
import {batchedEventUpdates} from './ReactDOMUpdateBatching';
import getListener from './getListener';
import {
enableLegacyFBSupport,
enableUseEventAPI,
enableScopeAPI,
} from 'shared/ReactFeatureFlags';
import {enableLegacyFBSupport} from 'shared/ReactFeatureFlags';
import {
invokeGuardedCallbackAndCatchFirstError,
rethrowCaughtError,
@@ -140,44 +124,8 @@ const capturePhaseEvents = new Set([
TOP_WAITING,
]);
if (enableUseEventAPI) {
capturePhaseEvents.add(TOP_BEFORE_BLUR);
capturePhaseEvents.add(TOP_AFTER_BLUR);
}
const emptyDispatchConfigForCustomEvents: CustomDispatchConfig = {
customEvent: true,
phasedRegistrationNames: {
bubbled: null,
captured: null,
},
};
const isArray = Array.isArray;
// TODO: we should remove the FlowFixMes and the casting to figure out how to make
// these patterns work properly.
// $FlowFixMe: Flow struggles with this pattern, so we also have to cast it.
const PossiblyWeakMap = ((typeof WeakMap === 'function' ? WeakMap : Map): any);
// $FlowFixMe: Flow cannot handle polymorphic WeakMaps
const eventTargetEventListenerStore: WeakMap<
EventTarget,
Map<
DOMTopLevelEventType,
{bubbled: Set<ReactDOMListener>, captured: Set<ReactDOMListener>},
>,
> = new PossiblyWeakMap();
// $FlowFixMe: Flow cannot handle polymorphic WeakMaps
const reactScopeListenerStore: WeakMap<
ReactScopeMethods,
Map<
DOMTopLevelEventType,
{bubbled: Set<ReactDOMListener>, captured: Set<ReactDOMListener>},
>,
> = new PossiblyWeakMap();
function executeDispatch(
event: ReactSyntheticEvent,
listener: Function,
@@ -514,183 +462,7 @@ export function dispatchEventForPluginEventSystem(
);
}
function getNearestRootOrPortalContainer(instance: Element): Element {
let node = getClosestInstanceFromNode(instance);
while (node !== null) {
const tag = node.tag;
// Once we encounter a host container or root container
// we can return their DOM instance.
if (tag === HostRoot || tag === HostPortal) {
return node.stateNode.containerInfo;
}
node = node.return;
}
return instance;
}
function addEventTypeToDispatchConfig(type: DOMTopLevelEventType): void {
const dispatchConfig = topLevelEventsToDispatchConfig.get(type);
// If we don't have a dispatchConfig, then we're dealing with
// an event type that React does not know about (i.e. a custom event).
// We need to register an event config for this or the SimpleEventPlugin
// will not appropriately provide a SyntheticEvent, so we use out empty
// dispatch config for custom events.
if (dispatchConfig === undefined) {
topLevelEventsToDispatchConfig.set(
type,
emptyDispatchConfigForCustomEvents,
);
}
}
export function attachListenerToManagedDOMElement(
listener: ReactDOMListener,
): void {
const {event, target} = listener;
const {passive, priority, type} = event;
const managedTargetElement = ((target: any): Element);
const containerEventTarget = getNearestRootOrPortalContainer(
managedTargetElement,
);
const listenerMap = getListenerMapForElement(containerEventTarget);
// Add the event listener to the target container (falling back to
// the target if we didn't find one).
listenToTopLevelEvent(
type,
containerEventTarget,
listenerMap,
PLUGIN_EVENT_SYSTEM | USE_EVENT_SYSTEM,
passive,
priority,
);
// Get the internal listeners Set from the target instance.
let listeners = getListenersFromTarget(managedTargetElement);
// If we don't have any listeners, then we need to init them.
if (listeners === null) {
listeners = new Set();
initListenersSet(managedTargetElement, listeners);
}
// Add our listener to the listeners Set.
listeners.add(listener);
// Finally, add the event to our known event types list.
addEventTypeToDispatchConfig(type);
}
export function detachListenerFromManagedDOMElement(
listener: ReactDOMListener,
): void {
const {target} = listener;
const managedTargetElement = ((target: any): Element);
// Get the internal listeners Set from the target instance.
const listeners = getListenersFromTarget(managedTargetElement);
if (listeners !== null) {
// Remove out listener from the listeners Set.
listeners.delete(listener);
}
}
export function attachTargetEventListener(listener: ReactDOMListener): void {
const {event, target} = listener;
const {capture, passive, priority, type} = event;
const eventTarget = ((target: any): EventTarget);
const listenerMap = getListenerMapForElement(eventTarget);
// Add the event listener to the TargetEvent object.
listenToTopLevelEvent(
type,
eventTarget,
listenerMap,
PLUGIN_EVENT_SYSTEM | USE_EVENT_SYSTEM | IS_TARGET_PHASE_ONLY,
passive,
priority,
capture,
);
let eventTypeMap = eventTargetEventListenerStore.get(eventTarget);
if (eventTypeMap === undefined) {
eventTypeMap = new Map();
eventTargetEventListenerStore.set(eventTarget, eventTypeMap);
}
// Get the listeners by the event type
let listeners = eventTypeMap.get(type);
if (listeners === undefined) {
listeners = {captured: new Set(), bubbled: new Set()};
eventTypeMap.set(type, listeners);
}
// Add our listener to the listeners Set.
if (capture) {
listeners.captured.add(listener);
} else {
listeners.bubbled.add(listener);
}
// Finally, add the event to our known event types list.
addEventTypeToDispatchConfig(type);
}
export function detachTargetEventListener(listener: ReactDOMListener): void {
const {event, target} = listener;
const {capture, type} = event;
const validEventTarget = ((target: any): EventTarget);
const eventTypeMap = eventTargetEventListenerStore.get(validEventTarget);
if (eventTypeMap !== undefined) {
const listeners = eventTypeMap.get(type);
if (listeners !== undefined) {
// Remove out listener from the listeners Set.
if (capture) {
listeners.captured.delete(listener);
} else {
listeners.bubbled.delete(listener);
}
}
}
}
export function attachListenerToReactScope(listener: ReactDOMListener): void {
const {event, target} = listener;
const {capture, type} = event;
const reactScope = ((target: any): ReactScopeMethods);
let eventTypeMap = reactScopeListenerStore.get(reactScope);
if (eventTypeMap === undefined) {
eventTypeMap = new Map();
reactScopeListenerStore.set(reactScope, eventTypeMap);
}
// Get the listeners by the event type
let listeners = eventTypeMap.get(type);
if (listeners === undefined) {
listeners = {captured: new Set(), bubbled: new Set()};
eventTypeMap.set(type, listeners);
}
// Add our listener to the listeners Set.
if (capture) {
listeners.captured.add(listener);
} else {
listeners.bubbled.add(listener);
}
// Finally, add the event to our known event types list.
addEventTypeToDispatchConfig(type);
}
export function detachListenerFromReactScope(listener: ReactDOMListener): void {
const {event, target} = listener;
const {capture, type} = event;
const reactScope = ((target: any): ReactScopeMethods);
const eventTypeMap = reactScopeListenerStore.get(reactScope);
if (eventTypeMap !== undefined) {
const listeners = eventTypeMap.get(type);
if (listeners !== undefined) {
// Remove out listener from the listeners Set.
if (capture) {
listeners.captured.delete(listener);
} else {
listeners.bubbled.delete(listener);
}
}
}
}
export function accumulateTwoPhaseListeners(
event: ReactSyntheticEvent,
accumulateUseEventListeners?: boolean,
): void {
export function accumulateTwoPhaseListeners(event: ReactSyntheticEvent): void {
const phasedRegistrationNames = event.dispatchConfig.phasedRegistrationNames;
const dispatchListeners = [];
const dispatchInstances: Array<Fiber | null> = [];
@@ -701,7 +473,6 @@ export function accumulateTwoPhaseListeners(
// usual two phase accumulation using the React fiber tree to pick up
// all relevant useEvent and on* prop events.
let instance = event._targetInst;
let lastHostComponent = null;
// Accumulate all instances and listeners via the target -> root path.
while (instance !== null) {
@@ -709,35 +480,6 @@ export function accumulateTwoPhaseListeners(
// Handle listeners that are on HostComponents (i.e. <div>)
if (tag === HostComponent && stateNode !== null) {
const currentTarget = stateNode;
lastHostComponent = currentTarget;
// For useEvent listenrs
if (enableUseEventAPI && accumulateUseEventListeners) {
// useEvent event listeners
const targetType = event.type;
const listeners = getListenersFromTarget(currentTarget);
if (listeners !== null) {
const listenersArr = Array.from(listeners);
for (let i = 0; i < listenersArr.length; i++) {
const listener = listenersArr[i];
const {
callback,
event: {capture, type},
} = listener;
if (type === targetType) {
if (capture === true) {
dispatchListeners.unshift(callback);
dispatchInstances.unshift(instance);
dispatchCurrentTargets.unshift(currentTarget);
} else {
dispatchListeners.push(callback);
dispatchInstances.push(instance);
dispatchCurrentTargets.push(currentTarget);
}
}
}
}
}
// Standard React on* listeners, i.e. onClick prop
if (captured !== null) {
const captureListener = getListener(instance, captured);
@@ -760,40 +502,6 @@ export function accumulateTwoPhaseListeners(
}
}
}
if (
enableUseEventAPI &&
enableScopeAPI &&
accumulateUseEventListeners &&
tag === ScopeComponent &&
lastHostComponent !== null
) {
const reactScope = stateNode.methods;
const eventTypeMap = reactScopeListenerStore.get(reactScope);
if (eventTypeMap !== undefined) {
const type = ((event.type: any): DOMTopLevelEventType);
const listeners = eventTypeMap.get(type);
if (listeners !== undefined) {
const captureListeners = Array.from(listeners.captured);
const bubbleListeners = Array.from(listeners.bubbled);
const lastCurrentTarget = ((lastHostComponent: any): Element);
for (let i = 0; i < captureListeners.length; i++) {
const listener = captureListeners[i];
const {callback} = listener;
dispatchListeners.unshift(callback);
dispatchInstances.unshift(instance);
dispatchCurrentTargets.unshift(lastCurrentTarget);
}
for (let i = 0; i < bubbleListeners.length; i++) {
const listener = bubbleListeners[i];
const {callback} = listener;
dispatchListeners.push(callback);
dispatchInstances.push(instance);
dispatchCurrentTargets.push(lastCurrentTarget);
}
}
}
}
instance = instance.return;
}
@@ -806,57 +514,6 @@ export function accumulateTwoPhaseListeners(
}
}
export function accumulateEventTargetListeners(
event: ReactSyntheticEvent,
currentTarget: EventTarget,
): void {
const dispatchListeners = [];
const dispatchInstances: Array<Fiber | null> = [];
const dispatchCurrentTargets = [];
const eventTypeMap = eventTargetEventListenerStore.get(currentTarget);
if (eventTypeMap !== undefined) {
const type = ((event.type: any): DOMTopLevelEventType);
const listeners = eventTypeMap.get(type);
if (listeners !== undefined) {
const isCapturePhase = (event: any).eventPhase === 1;
if (isCapturePhase) {
const captureListeners = Array.from(listeners.captured);
for (let i = captureListeners.length - 1; i >= 0; i--) {
const listener = captureListeners[i];
const {callback} = listener;
dispatchListeners.push(callback);
// EventTarget listeners do not have instances, as there
// is no backing Fiber instance for them (window, document etc).
dispatchInstances.push(null);
dispatchCurrentTargets.push(currentTarget);
}
} else {
const bubbleListeners = Array.from(listeners.bubbled);
for (let i = 0; i < bubbleListeners.length; i++) {
const listener = bubbleListeners[i];
const {callback} = listener;
dispatchListeners.push(callback);
// EventTarget listeners do not have instances, as there
// is no backing Fiber instance for them (window, document etc).
dispatchInstances.push(null);
dispatchCurrentTargets.push(currentTarget);
}
}
}
}
// To prevent allocation to the event unless we actually
// have listeners we check the length of one of the arrays.
if (dispatchListeners.length > 0) {
event._dispatchListeners = dispatchListeners;
event._dispatchInstances = dispatchInstances;
event._dispatchCurrentTargets = dispatchCurrentTargets;
}
}
function getParent(inst: Fiber | null): Fiber | null {
if (inst === null) {
return null;
+10 -32
View File
@@ -44,7 +44,6 @@ import {
addEventBubbleListener,
addEventCaptureListener,
addEventCaptureListenerWithPassiveFlag,
addEventBubbleListenerWithPassiveFlag,
removeEventListener,
} from './EventListener';
import getEventTarget from './getEventTarget';
@@ -56,7 +55,6 @@ import {
enableDeprecatedFlareAPI,
enableModernEventSystem,
enableLegacyFBSupport,
enableUseEventAPI,
} from 'shared/ReactFeatureFlags';
import {
UserBlockingEvent,
@@ -200,37 +198,17 @@ export function addTrappedEventListener(
};
}
if (capture) {
if (enableUseEventAPI && passive !== undefined) {
// This is only used with passive is either true or false.
unsubscribeListener = addEventCaptureListenerWithPassiveFlag(
targetContainer,
rawEventName,
listener,
passive,
);
} else {
unsubscribeListener = addEventCaptureListener(
targetContainer,
rawEventName,
listener,
);
}
unsubscribeListener = addEventCaptureListener(
targetContainer,
rawEventName,
listener,
);
} else {
if (enableUseEventAPI && passive !== undefined) {
// This is only used with passive is either true or false.
unsubscribeListener = addEventBubbleListenerWithPassiveFlag(
targetContainer,
rawEventName,
listener,
passive,
);
} else {
unsubscribeListener = addEventBubbleListener(
targetContainer,
rawEventName,
listener,
);
}
unsubscribeListener = addEventBubbleListener(
targetContainer,
rawEventName,
listener,
);
}
return unsubscribeListener;
}
@@ -17,17 +17,13 @@ import type {PluginModule} from 'legacy-events/PluginModuleType';
import type {EventSystemFlags} from '../EventSystemFlags';
import SyntheticEvent from 'legacy-events/SyntheticEvent';
import {IS_TARGET_PHASE_ONLY} from '../EventSystemFlags';
import * as DOMTopLevelEventTypes from '../DOMTopLevelEventTypes';
import {
topLevelEventsToDispatchConfig,
simpleEventPluginEventTypes,
} from '../DOMEventProperties';
import {
accumulateEventTargetListeners,
accumulateTwoPhaseListeners,
} from '../DOMModernPluginEventSystem';
import {accumulateTwoPhaseListeners} from '../DOMModernPluginEventSystem';
import SyntheticAnimationEvent from '../SyntheticAnimationEvent';
import SyntheticClipboardEvent from '../SyntheticClipboardEvent';
@@ -42,8 +38,6 @@ import SyntheticUIEvent from '../SyntheticUIEvent';
import SyntheticWheelEvent from '../SyntheticWheelEvent';
import getEventCharCode from '../getEventCharCode';
import {enableUseEventAPI} from 'shared/ReactFeatureFlags';
// Only used in DEV for exhaustiveness validation.
const knownHTMLTopLevelTypes: Array<DOMTopLevelEventType> = [
DOMTopLevelEventTypes.TOP_ABORT,
@@ -204,22 +198,7 @@ const SimpleEventPlugin: PluginModule<MouseEvent> = {
nativeEventTarget,
);
// For TargetEvent only accumulation, we do not traverse through
// the React tree looking for managed React DOM elements that have
// events. Instead we only check the EventTarget Store Map to see
// if the container has listeners for the particular phase we're
// interested in. This is because we attach the native event listener
// only in the given phase.
if (
enableUseEventAPI &&
eventSystemFlags !== undefined &&
eventSystemFlags & IS_TARGET_PHASE_ONLY &&
targetContainer != null
) {
accumulateEventTargetListeners(event, targetContainer);
} else {
accumulateTwoPhaseListeners(event, true);
}
accumulateTwoPhaseListeners(event);
return event;
},
};
@@ -19,7 +19,6 @@ import type {
ReactEventResponderListener,
} from 'shared/ReactTypes';
import type {SuspenseConfig} from 'react-reconciler/src/ReactFiberSuspenseConfig';
import type {ReactDOMListenerMap} from '../shared/ReactDOMTypes';
import {validateContextBounds} from './ReactPartialRendererContext';
import {makeServerId} from '../client/ReactDOMHostConfig';
@@ -499,13 +498,6 @@ function useOpaqueIdentifier(): OpaqueIDType {
return makeServerId();
}
function useEvent(event: any): ReactDOMListenerMap {
return {
clear: noop,
setListener: noop,
};
}
function noop(): void {}
export let currentThreadID: ThreadID = 0;
@@ -532,7 +524,6 @@ export const Dispatcher: DispatcherType = {
useResponder,
useDeferredValue,
useTransition,
useEvent,
useOpaqueIdentifier,
// Subscriptions are not setup in a server environment.
useMutableSource,
-24
View File
@@ -12,9 +12,7 @@ import type {
ReactEventResponder,
ReactEventResponderInstance,
EventPriority,
ReactScopeMethods,
} from 'shared/ReactTypes';
import type {DOMTopLevelEventType} from 'legacy-events/TopLevelEventTypes';
type AnyNativeEvent = Event | KeyboardEvent | MouseEvent | Touch;
@@ -77,25 +75,3 @@ export type ReactDOMResponderContext = {
getResponderNode(): Element | null,
...
};
export type ReactDOMListenerEvent = {|
capture: boolean,
passive: void | boolean,
priority: EventPriority,
type: DOMTopLevelEventType,
|};
export type ReactDOMListenerMap = {|
clear: () => void,
setListener: (
target: EventTarget | ReactScopeMethods,
callback: ?(SyntheticEvent<EventTarget>) => void,
) => void,
|};
export type ReactDOMListener = {|
callback: (SyntheticEvent<EventTarget>) => void,
destroy: Node => void,
event: ReactDOMListenerEvent,
target: EventTarget | ReactScopeMethods,
|};
-10
View File
@@ -1,10 +0,0 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
export * from './src/dom/use-event/Focus';
@@ -1,440 +0,0 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
import * as React from 'react';
import * as ReactDOM from 'react-dom';
const useEvent = ReactDOM.unstable_useEvent;
const {useEffect, useRef} = React;
type UseFocusOptions = {|
disabled?: boolean,
onBlur?: (SyntheticEvent<EventTarget>) => void,
onFocus?: (SyntheticEvent<EventTarget>) => void,
onFocusChange?: boolean => void,
onFocusVisibleChange?: boolean => void,
|};
type UseFocusWithinOptions = {|
disabled?: boolean,
onAfterBlurWithin?: (SyntheticEvent<EventTarget>) => void,
onBeforeBlurWithin?: (SyntheticEvent<EventTarget>) => void,
onBlurWithin?: (SyntheticEvent<EventTarget>) => void,
onFocusWithin?: (SyntheticEvent<EventTarget>) => void,
onFocusWithinChange?: boolean => void,
onFocusWithinVisibleChange?: boolean => void,
|};
const isMac =
typeof window !== 'undefined' && window.navigator != null
? /^Mac/.test(window.navigator.platform)
: false;
const canUseDOM: boolean = !!(
typeof window !== 'undefined' &&
typeof window.document !== 'undefined' &&
typeof window.document.createElement !== 'undefined'
);
let passiveBrowserEventsSupported = false;
// Check if browser support events with passive listeners
// https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support
if (canUseDOM) {
try {
const options = {};
// $FlowFixMe: Ignore Flow complaining about needing a value
Object.defineProperty(options, 'passive', {
get: function() {
passiveBrowserEventsSupported = true;
},
});
window.addEventListener('test', options, options);
window.removeEventListener('test', options, options);
} catch (e) {
passiveBrowserEventsSupported = false;
}
}
const hasPointerEvents =
typeof window !== 'undefined' && window.PointerEvent != null;
const globalFocusVisibleEvents = hasPointerEvents
? ['keydown', 'pointermove', 'pointerdown', 'pointerup']
: ['keydown', 'mousedown', 'touchmove', 'touchstart', 'touchend'];
// Global state for tracking focus visible and emulation of mouse
let isGlobalFocusVisible = true;
let hasTrackedGlobalFocusVisible = false;
let isEmulatingMouseEvents = false;
function trackGlobalFocusVisible() {
globalFocusVisibleEvents.forEach(type => {
window.addEventListener(
type,
handleGlobalFocusVisibleEvent,
passiveBrowserEventsSupported ? {capture: true, passive: true} : true,
);
});
}
function handleGlobalFocusVisibleEvent(
nativeEvent: MouseEvent | TouchEvent | KeyboardEvent,
): void {
const {type} = nativeEvent;
switch (type) {
case 'pointermove':
case 'pointerdown':
case 'pointerup': {
isGlobalFocusVisible = false;
break;
}
case 'keydown': {
const {metaKey, altKey, ctrlKey} = nativeEvent;
const validKey = !(metaKey || (!isMac && altKey) || ctrlKey);
if (validKey) {
isGlobalFocusVisible = true;
}
break;
}
// fallbacks for no PointerEvent support
case 'touchmove':
case 'touchstart':
case 'touchend': {
isEmulatingMouseEvents = true;
isGlobalFocusVisible = false;
break;
}
case 'mousedown': {
if (!isEmulatingMouseEvents) {
isGlobalFocusVisible = false;
} else {
isEmulatingMouseEvents = false;
}
break;
}
}
}
const passiveObject = {passive: true};
function handleFocusVisibleTargetEvent(
type: string,
focusTarget: EventTarget,
callback: boolean => void,
): void {
isGlobalFocusVisible = false;
// Focus should stop being visible if a pointer is used on the element
// after it was focused using a keyboard.
if (
focusTarget !== null &&
(type === 'mousedown' || type === 'touchstart' || type === 'pointerdown')
) {
callback(false);
}
}
function handleFocusVisibleTargetEvents(
event: SyntheticEvent<EventTarget>,
focusTarget,
callback,
): void {
const {type} = event;
switch (type) {
case 'pointermove':
case 'pointerdown':
case 'pointerup': {
handleFocusVisibleTargetEvent(type, focusTarget, callback);
break;
}
case 'keydown':
case 'keyup': {
const {metaKey, altKey, ctrlKey} = (event: any);
const validKey = !(metaKey || (!isMac && altKey) || ctrlKey);
if (validKey) {
if (focusTarget !== null) {
callback(true);
}
}
break;
}
// fallbacks for no PointerEvent support
case 'touchmove':
case 'touchstart':
case 'touchend': {
handleFocusVisibleTargetEvent(type, focusTarget, callback);
break;
}
case 'mousedown': {
if (!isEmulatingMouseEvents) {
handleFocusVisibleTargetEvent(type, focusTarget, callback);
}
break;
}
}
}
function isRelatedTargetWithin(
focusWithinTarget: Node,
relatedTarget: null | Node,
): boolean {
if (relatedTarget == null) {
return false;
}
// To support experimental scopes, which can be the target:
const containsNode = (focusWithinTarget: any).containsNode;
if (typeof containsNode === 'function') {
return containsNode(relatedTarget);
}
return focusWithinTarget.contains(relatedTarget);
}
function setFocusVisibleListeners(focusVisibleHandles, focusTarget, callback) {
focusVisibleHandles.forEach(focusVisibleHandle => {
focusVisibleHandle.setListener(focusTarget, event =>
handleFocusVisibleTargetEvents(event, focusTarget, callback),
);
});
}
function useFocusVisibleInputHandles() {
return [
useEvent('mousedown', passiveObject),
useEvent(hasPointerEvents ? 'pointerdown' : 'touchstart', passiveObject),
useEvent(hasPointerEvents ? 'pointermove' : 'touchmove', passiveObject),
useEvent(hasPointerEvents ? 'pointerup' : 'touchend', passiveObject),
useEvent('keydown', passiveObject),
];
}
function useFocusLifecycles(stateRef) {
useEffect(() => {
if (!hasTrackedGlobalFocusVisible) {
hasTrackedGlobalFocusVisible = true;
trackGlobalFocusVisible();
}
}, []);
}
export function useFocus(
focusTargetRef: {current: null | Node},
{
disabled,
onBlur,
onFocus,
onFocusChange,
onFocusVisibleChange,
}: UseFocusOptions,
): void {
// Setup controlled state for this useFocus hook
const stateRef = useRef({isFocused: false, isFocusVisible: false});
const focusHandle = useEvent('focus', passiveObject);
const blurHandle = useEvent('blur', passiveObject);
const focusVisibleHandles = useFocusVisibleInputHandles();
useEffect(() => {
const focusTarget = focusTargetRef.current;
const state = stateRef.current;
if (focusTarget !== null && state !== null) {
// Handle focus visible
setFocusVisibleListeners(
focusVisibleHandles,
focusTarget,
isFocusVisible => {
if (state.isFocused && state.isFocusVisible !== isFocusVisible) {
state.isFocusVisible = isFocusVisible;
if (onFocusVisibleChange) {
onFocusVisibleChange(isFocusVisible);
}
}
},
);
// Handle focus
focusHandle.setListener(focusTarget, event => {
if (disabled) {
return;
}
// Limit focus events to the direct child of the event component.
// Browser focus is not expected to bubble.
if (!state.isFocused && focusTarget === event.target) {
state.isFocused = true;
state.isFocusVisible = isGlobalFocusVisible;
if (onFocus) {
onFocus(event);
}
if (onFocusChange) {
onFocusChange(true);
}
if (state.isFocusVisible && onFocusVisibleChange) {
onFocusVisibleChange(true);
}
isEmulatingMouseEvents = false;
}
});
// Handle blur
blurHandle.setListener(focusTarget, event => {
if (disabled) {
return;
}
if (state.isFocused) {
state.isFocused = false;
state.isFocusVisible = isGlobalFocusVisible;
if (onBlur) {
onBlur(event);
}
if (onFocusChange) {
onFocusChange(false);
}
if (state.isFocusVisible && onFocusVisibleChange) {
onFocusVisibleChange(false);
}
}
isEmulatingMouseEvents = false;
});
}
}, [disabled, onBlur, onFocus, onFocusChange, onFocusVisibleChange]);
// Mount/Unmount logic
useFocusLifecycles(stateRef);
}
export function useFocusWithin(
focusWithinTargetRef: {current: null | Node},
{
disabled,
onAfterBlurWithin,
onBeforeBlurWithin,
onBlurWithin,
onFocusWithin,
onFocusWithinChange,
onFocusWithinVisibleChange,
}: UseFocusWithinOptions,
) {
// Setup controlled state for this useFocus hook
const stateRef = useRef({isFocused: false, isFocusVisible: false});
const focusHandle = useEvent('focus', passiveObject);
const blurHandle = useEvent('blur', passiveObject);
const afterBlurHandle = useEvent('afterblur', passiveObject);
const beforeBlurHandle = useEvent('beforeblur', passiveObject);
const focusVisibleHandles = useFocusVisibleInputHandles();
useEffect(() => {
const focusWithinTarget = focusWithinTargetRef.current;
const state = stateRef.current;
if (focusWithinTarget !== null && state !== null) {
// Handle focus visible
setFocusVisibleListeners(
focusVisibleHandles,
focusWithinTarget,
isFocusVisible => {
if (state.isFocused && state.isFocusVisible !== isFocusVisible) {
state.isFocusVisible = isFocusVisible;
if (onFocusWithinVisibleChange) {
onFocusWithinVisibleChange(isFocusVisible);
}
}
},
);
// Handle focus
focusHandle.setListener(focusWithinTarget, event => {
if (disabled) {
return;
}
if (!state.isFocused) {
state.isFocused = true;
state.isFocusVisible = isGlobalFocusVisible;
if (onFocusWithinChange) {
onFocusWithinChange(true);
}
if (state.isFocusVisible && onFocusWithinVisibleChange) {
onFocusWithinVisibleChange(true);
}
}
if (!state.isFocusVisible && isGlobalFocusVisible) {
state.isFocusVisible = isGlobalFocusVisible;
if (onFocusWithinVisibleChange) {
onFocusWithinVisibleChange(true);
}
}
if (onFocusWithin) {
onFocusWithin(event);
}
isEmulatingMouseEvents = false;
});
// Handle blur
blurHandle.setListener(focusWithinTarget, event => {
if (disabled) {
return;
}
const {relatedTarget} = (event: any);
if (
state.isFocused &&
!isRelatedTargetWithin(focusWithinTarget, relatedTarget)
) {
state.isFocused = false;
if (onFocusWithinChange) {
onFocusWithinChange(false);
}
if (state.isFocusVisible && onFocusWithinVisibleChange) {
onFocusWithinVisibleChange(false);
}
if (onBlurWithin) {
onBlurWithin(event);
}
}
isEmulatingMouseEvents = false;
});
// Handle before blur. This is a special
// React provided event.
beforeBlurHandle.setListener(focusWithinTarget, event => {
if (disabled) {
return;
}
if (onBeforeBlurWithin) {
onBeforeBlurWithin(event);
// Add an "afterblur" listener on document. This is a special
// React provided event.
afterBlurHandle.setListener(document, afterBlurEvent => {
if (onAfterBlurWithin) {
onAfterBlurWithin(afterBlurEvent);
}
// Clear listener on document
afterBlurHandle.setListener(document, null);
});
}
});
}
}, [
disabled,
onBlurWithin,
onFocusWithin,
onFocusWithinChange,
onFocusWithinVisibleChange,
]);
// Mount/Unmount logic
useFocusLifecycles(stateRef);
}
@@ -1,319 +0,0 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @emails react-core
*/
'use strict';
import {createEventTarget, setPointerEvent} from 'dom-event-testing-library';
let React;
let ReactFeatureFlags;
let ReactDOM;
let useFocus;
function initializeModules(hasPointerEvents) {
setPointerEvent(hasPointerEvents);
jest.resetModules();
ReactFeatureFlags = require('shared/ReactFeatureFlags');
ReactFeatureFlags.enableModernEventSystem = true;
ReactFeatureFlags.enableUseEventAPI = true;
React = require('react');
ReactDOM = require('react-dom');
// TODO: This import throws outside of experimental mode. Figure out better
// strategy for gated imports.
if (__EXPERIMENTAL__) {
useFocus = require('react-interactions/events/focus').useFocus;
}
}
const forcePointerEvents = true;
const table = [[forcePointerEvents], [!forcePointerEvents]];
describe.each(table)(`useFocus`, hasPointerEvents => {
let container;
beforeEach(() => {
initializeModules(hasPointerEvents);
container = document.createElement('div');
document.body.appendChild(container);
});
afterEach(() => {
ReactDOM.render(null, container);
document.body.removeChild(container);
container = null;
});
describe('disabled', () => {
let onBlur, onFocus, ref;
const componentInit = () => {
onBlur = jest.fn();
onFocus = jest.fn();
ref = React.createRef();
const Component = () => {
useFocus(ref, {
disabled: true,
onBlur,
onFocus,
});
return <div ref={ref} />;
};
ReactDOM.render(<Component />, container);
};
// @gate experimental
it('does not call callbacks', () => {
componentInit();
const target = createEventTarget(ref.current);
target.focus();
target.blur();
expect(onFocus).not.toBeCalled();
expect(onBlur).not.toBeCalled();
});
});
describe('onBlur', () => {
let onBlur, ref;
const componentInit = () => {
onBlur = jest.fn();
ref = React.createRef();
const Component = () => {
useFocus(ref, {
onBlur,
});
return <div ref={ref} />;
};
ReactDOM.render(<Component />, container);
};
// @gate experimental
it('is called after "blur" event', () => {
componentInit();
const target = createEventTarget(ref.current);
target.focus();
target.blur();
expect(onBlur).toHaveBeenCalledTimes(1);
});
});
describe('onFocus', () => {
let onFocus, ref, innerRef;
const componentInit = () => {
onFocus = jest.fn();
ref = React.createRef();
innerRef = React.createRef();
const Component = () => {
useFocus(ref, {
onFocus,
});
return (
<div ref={ref}>
<a ref={innerRef} />
</div>
);
};
ReactDOM.render(<Component />, container);
};
// @gate experimental
it('is called after "focus" event', () => {
componentInit();
const target = createEventTarget(ref.current);
target.focus();
expect(onFocus).toHaveBeenCalledTimes(1);
});
// @gate experimental
it('is not called if descendants of target receive focus', () => {
componentInit();
const target = createEventTarget(innerRef.current);
target.focus();
expect(onFocus).not.toBeCalled();
});
});
describe('onFocusChange', () => {
let onFocusChange, ref, innerRef;
const componentInit = () => {
onFocusChange = jest.fn();
ref = React.createRef();
innerRef = React.createRef();
const Component = () => {
useFocus(ref, {
onFocusChange,
});
return (
<div ref={ref}>
<div ref={innerRef} />
</div>
);
};
ReactDOM.render(<Component />, container);
};
// @gate experimental
it('is called after "blur" and "focus" events', () => {
componentInit();
const target = createEventTarget(ref.current);
target.focus();
expect(onFocusChange).toHaveBeenCalledTimes(1);
expect(onFocusChange).toHaveBeenCalledWith(true);
target.blur();
expect(onFocusChange).toHaveBeenCalledTimes(2);
expect(onFocusChange).toHaveBeenCalledWith(false);
});
// @gate experimental
it('is not called after "blur" and "focus" events on descendants', () => {
componentInit();
const target = createEventTarget(innerRef.current);
target.focus();
expect(onFocusChange).toHaveBeenCalledTimes(0);
target.blur();
expect(onFocusChange).toHaveBeenCalledTimes(0);
});
});
describe('onFocusVisibleChange', () => {
let onFocusVisibleChange, ref, innerRef;
const componentInit = () => {
onFocusVisibleChange = jest.fn();
ref = React.createRef();
innerRef = React.createRef();
const Component = () => {
useFocus(ref, {
onFocusVisibleChange,
});
return (
<div ref={ref}>
<div ref={innerRef} />
</div>
);
};
ReactDOM.render(<Component />, container);
};
// @gate experimental
it('is called after "focus" and "blur" if keyboard navigation is active', () => {
componentInit();
const target = createEventTarget(ref.current);
const containerTarget = createEventTarget(container);
// use keyboard first
containerTarget.keydown({key: 'Tab'});
target.focus();
expect(onFocusVisibleChange).toHaveBeenCalledTimes(1);
expect(onFocusVisibleChange).toHaveBeenCalledWith(true);
target.blur({relatedTarget: container});
expect(onFocusVisibleChange).toHaveBeenCalledTimes(2);
expect(onFocusVisibleChange).toHaveBeenCalledWith(false);
});
// @gate experimental
it('is called if non-keyboard event is dispatched on target previously focused with keyboard', () => {
componentInit();
const target = createEventTarget(ref.current);
const containerTarget = createEventTarget(container);
// use keyboard first
containerTarget.keydown({key: 'Tab'});
target.focus();
expect(onFocusVisibleChange).toHaveBeenCalledTimes(1);
expect(onFocusVisibleChange).toHaveBeenCalledWith(true);
// then use pointer on the target, focus should no longer be visible
target.pointerdown();
expect(onFocusVisibleChange).toHaveBeenCalledTimes(2);
expect(onFocusVisibleChange).toHaveBeenCalledWith(false);
// onFocusVisibleChange should not be called again
target.blur({relatedTarget: container});
expect(onFocusVisibleChange).toHaveBeenCalledTimes(2);
});
// @gate experimental
it('is not called after "focus" and "blur" events without keyboard', () => {
componentInit();
const target = createEventTarget(ref.current);
const containerTarget = createEventTarget(container);
target.pointerdown();
target.pointerup();
containerTarget.pointerdown();
target.blur({relatedTarget: container});
expect(onFocusVisibleChange).toHaveBeenCalledTimes(0);
});
// @gate experimental
it('is not called after "blur" and "focus" events on descendants', () => {
componentInit();
const innerTarget = createEventTarget(innerRef.current);
const containerTarget = createEventTarget(container);
containerTarget.keydown({key: 'Tab'});
innerTarget.focus();
expect(onFocusVisibleChange).toHaveBeenCalledTimes(0);
innerTarget.blur({relatedTarget: container});
expect(onFocusVisibleChange).toHaveBeenCalledTimes(0);
});
});
describe('nested Focus components', () => {
// @gate experimental
it('propagates events in the correct order', () => {
const events = [];
const innerRef = React.createRef();
const outerRef = React.createRef();
const createEventHandler = msg => () => {
events.push(msg);
};
const Inner = () => {
useFocus(innerRef, {
onBlur: createEventHandler('inner: onBlur'),
onFocus: createEventHandler('inner: onFocus'),
onFocusChange: createEventHandler('inner: onFocusChange'),
});
return <div ref={innerRef} />;
};
const Outer = () => {
useFocus(outerRef, {
onBlur: createEventHandler('outer: onBlur'),
onFocus: createEventHandler('outer: onFocus'),
onFocusChange: createEventHandler('outer: onFocusChange'),
});
return (
<div ref={outerRef}>
<Inner />
</div>
);
};
ReactDOM.render(<Outer />, container);
const innerTarget = createEventTarget(innerRef.current);
const outerTarget = createEventTarget(outerRef.current);
outerTarget.focus();
outerTarget.blur();
innerTarget.focus();
innerTarget.blur();
expect(events).toEqual([
'outer: onFocus',
'outer: onFocusChange',
'outer: onBlur',
'outer: onFocusChange',
'inner: onFocus',
'inner: onFocusChange',
'inner: onBlur',
'inner: onFocusChange',
]);
});
});
});
@@ -1,569 +0,0 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @emails react-core
*/
'use strict';
import {createEventTarget, setPointerEvent} from 'dom-event-testing-library';
let React;
let ReactFeatureFlags;
let ReactDOM;
let useFocusWithin;
let ReactTestRenderer;
let act;
function initializeModules(hasPointerEvents) {
setPointerEvent(hasPointerEvents);
jest.resetModules();
ReactFeatureFlags = require('shared/ReactFeatureFlags');
ReactFeatureFlags.enableScopeAPI = true;
ReactFeatureFlags.enableModernEventSystem = true;
ReactFeatureFlags.enableUseEventAPI = true;
React = require('react');
ReactDOM = require('react-dom');
ReactTestRenderer = require('react-test-renderer');
act = ReactTestRenderer.act;
// TODO: This import throws outside of experimental mode. Figure out better
// strategy for gated imports.
if (__EXPERIMENTAL__) {
useFocusWithin = require('react-interactions/events/focus').useFocusWithin;
}
}
const forcePointerEvents = true;
const table = [[forcePointerEvents], [!forcePointerEvents]];
describe.each(table)(`useFocus`, hasPointerEvents => {
let container;
let container2;
beforeEach(() => {
initializeModules(hasPointerEvents);
container = document.createElement('div');
document.body.appendChild(container);
container2 = document.createElement('div');
document.body.appendChild(container2);
});
afterEach(() => {
ReactDOM.render(null, container);
document.body.removeChild(container);
document.body.removeChild(container2);
container = null;
container2 = null;
});
describe('disabled', () => {
let onFocusWithinChange, onFocusWithinVisibleChange, ref;
const componentInit = () => {
onFocusWithinChange = jest.fn();
onFocusWithinVisibleChange = jest.fn();
ref = React.createRef();
const Component = () => {
useFocusWithin(ref, {
disabled: true,
onFocusWithinChange,
onFocusWithinVisibleChange,
});
return <div ref={ref} />;
};
ReactDOM.render(<Component />, container);
};
// @gate experimental
it('prevents custom events being dispatched', () => {
componentInit();
const target = createEventTarget(ref.current);
target.focus();
target.blur();
expect(onFocusWithinChange).not.toBeCalled();
expect(onFocusWithinVisibleChange).not.toBeCalled();
});
});
describe('onFocusWithinChange', () => {
let onFocusWithinChange, ref, innerRef, innerRef2;
const Component = ({show}) => {
useFocusWithin(ref, {
onFocusWithinChange,
});
return (
<div ref={ref}>
{show && <input ref={innerRef} />}
<div ref={innerRef2} />
</div>
);
};
const componentInit = () => {
onFocusWithinChange = jest.fn();
ref = React.createRef();
innerRef = React.createRef();
innerRef2 = React.createRef();
ReactDOM.render(<Component show={true} />, container);
};
// @gate experimental
it('is called after "blur" and "focus" events on focus target', () => {
componentInit();
const target = createEventTarget(ref.current);
target.focus();
expect(onFocusWithinChange).toHaveBeenCalledTimes(1);
expect(onFocusWithinChange).toHaveBeenCalledWith(true);
target.blur({relatedTarget: container});
expect(onFocusWithinChange).toHaveBeenCalledTimes(2);
expect(onFocusWithinChange).toHaveBeenCalledWith(false);
});
// @gate experimental
it('is called after "blur" and "focus" events on descendants', () => {
componentInit();
const target = createEventTarget(innerRef.current);
target.focus();
expect(onFocusWithinChange).toHaveBeenCalledTimes(1);
expect(onFocusWithinChange).toHaveBeenCalledWith(true);
target.blur({relatedTarget: container});
expect(onFocusWithinChange).toHaveBeenCalledTimes(2);
expect(onFocusWithinChange).toHaveBeenCalledWith(false);
});
// @gate experimental
it('is only called once when focus moves within and outside the subtree', () => {
componentInit();
const node = ref.current;
const innerNode1 = innerRef.current;
const innerNode2 = innerRef.current;
const target = createEventTarget(node);
const innerTarget1 = createEventTarget(innerNode1);
const innerTarget2 = createEventTarget(innerNode2);
// focus shifts into subtree
innerTarget1.focus();
expect(onFocusWithinChange).toHaveBeenCalledTimes(1);
expect(onFocusWithinChange).toHaveBeenCalledWith(true);
// focus moves around subtree
innerTarget1.blur({relatedTarget: innerNode2});
innerTarget2.focus();
innerTarget2.blur({relatedTarget: node});
target.focus();
target.blur({relatedTarget: innerNode1});
expect(onFocusWithinChange).toHaveBeenCalledTimes(1);
// focus shifts outside subtree
innerTarget1.blur({relatedTarget: container});
expect(onFocusWithinChange).toHaveBeenCalledTimes(2);
expect(onFocusWithinChange).toHaveBeenCalledWith(false);
});
});
describe('onFocusWithinVisibleChange', () => {
let onFocusWithinVisibleChange, ref, innerRef, innerRef2;
const Component = ({show}) => {
useFocusWithin(ref, {
onFocusWithinVisibleChange,
});
return (
<div ref={ref}>
{show && <input ref={innerRef} />}
<div ref={innerRef2} />
</div>
);
};
const componentInit = () => {
onFocusWithinVisibleChange = jest.fn();
ref = React.createRef();
innerRef = React.createRef();
innerRef2 = React.createRef();
ReactDOM.render(<Component show={true} />, container);
};
// @gate experimental
it('is called after "focus" and "blur" on focus target if keyboard was used', () => {
componentInit();
const target = createEventTarget(ref.current);
const containerTarget = createEventTarget(container);
// use keyboard first
containerTarget.keydown({key: 'Tab'});
target.focus();
expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(1);
expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(true);
target.blur({relatedTarget: container});
expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(2);
expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(false);
});
// @gate experimental
it('is called after "focus" and "blur" on descendants if keyboard was used', () => {
componentInit();
const innerTarget = createEventTarget(innerRef.current);
const containerTarget = createEventTarget(container);
// use keyboard first
containerTarget.keydown({key: 'Tab'});
innerTarget.focus();
expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(1);
expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(true);
innerTarget.blur({relatedTarget: container});
expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(2);
expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(false);
});
// @gate experimental
it('is called if non-keyboard event is dispatched on target previously focused with keyboard', () => {
componentInit();
const node = ref.current;
const innerNode1 = innerRef.current;
const innerNode2 = innerRef2.current;
const target = createEventTarget(node);
const innerTarget1 = createEventTarget(innerNode1);
const innerTarget2 = createEventTarget(innerNode2);
// use keyboard first
target.focus();
target.keydown({key: 'Tab'});
target.blur({relatedTarget: innerNode1});
innerTarget1.focus();
expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(1);
expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(true);
// then use pointer on the next target, focus should no longer be visible
innerTarget2.pointerdown();
innerTarget1.blur({relatedTarget: innerNode2});
innerTarget2.focus();
expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(2);
expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(false);
// then use keyboard again
innerTarget2.keydown({key: 'Tab', shiftKey: true});
innerTarget2.blur({relatedTarget: innerNode1});
innerTarget1.focus();
expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(3);
expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(true);
// then use pointer on the target, focus should no longer be visible
innerTarget1.pointerdown();
expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(4);
expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(false);
// onFocusVisibleChange should not be called again
innerTarget1.blur({relatedTarget: container});
expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(4);
});
// @gate experimental
it('is not called after "focus" and "blur" events without keyboard', () => {
componentInit();
const innerTarget = createEventTarget(innerRef.current);
innerTarget.pointerdown();
innerTarget.pointerup();
innerTarget.blur({relatedTarget: container});
expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(0);
});
// @gate experimental
it('is only called once when focus moves within and outside the subtree', () => {
componentInit();
const node = ref.current;
const innerNode1 = innerRef.current;
const innerNode2 = innerRef2.current;
const target = createEventTarget(node);
const innerTarget1 = createEventTarget(innerNode1);
const innerTarget2 = createEventTarget(innerNode2);
// focus shifts into subtree
innerTarget1.focus();
expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(1);
expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(true);
// focus moves around subtree
innerTarget1.blur({relatedTarget: innerNode2});
innerTarget2.focus();
innerTarget2.blur({relatedTarget: node});
target.focus();
target.blur({relatedTarget: innerNode1});
expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(1);
// focus shifts outside subtree
innerTarget1.blur({relatedTarget: container});
expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(2);
expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(false);
});
});
describe('onBeforeBlurWithin', () => {
let onBeforeBlurWithin, onAfterBlurWithin, ref, innerRef, innerRef2;
beforeEach(() => {
onBeforeBlurWithin = jest.fn();
onAfterBlurWithin = jest.fn(e => {
e.persist();
});
ref = React.createRef();
innerRef = React.createRef();
innerRef2 = React.createRef();
});
// @gate experimental
it('is called after a focused element is unmounted', () => {
const Component = ({show}) => {
useFocusWithin(ref, {
onBeforeBlurWithin,
onAfterBlurWithin,
});
return (
<div ref={ref}>
{show && <input ref={innerRef} />}
<div ref={innerRef2} />
</div>
);
};
ReactDOM.render(<Component show={true} />, container);
const inner = innerRef.current;
const target = createEventTarget(inner);
target.keydown({key: 'Tab'});
target.focus();
expect(onBeforeBlurWithin).toHaveBeenCalledTimes(0);
expect(onAfterBlurWithin).toHaveBeenCalledTimes(0);
ReactDOM.render(<Component show={false} />, container);
expect(onBeforeBlurWithin).toHaveBeenCalledTimes(1);
expect(onAfterBlurWithin).toHaveBeenCalledTimes(1);
expect(onAfterBlurWithin).toHaveBeenCalledWith(
expect.objectContaining({relatedTarget: inner}),
);
});
// @gate experimental
it('is called after a nested focused element is unmounted', () => {
const Component = ({show}) => {
useFocusWithin(ref, {
onBeforeBlurWithin,
onAfterBlurWithin,
});
return (
<div ref={ref}>
{show && (
<div>
<input ref={innerRef} />
</div>
)}
<div ref={innerRef2} />
</div>
);
};
ReactDOM.render(<Component show={true} />, container);
const inner = innerRef.current;
const target = createEventTarget(inner);
target.keydown({key: 'Tab'});
target.focus();
expect(onBeforeBlurWithin).toHaveBeenCalledTimes(0);
expect(onAfterBlurWithin).toHaveBeenCalledTimes(0);
ReactDOM.render(<Component show={false} />, container);
expect(onBeforeBlurWithin).toHaveBeenCalledTimes(1);
expect(onAfterBlurWithin).toHaveBeenCalledTimes(1);
expect(onAfterBlurWithin).toHaveBeenCalledWith(
expect.objectContaining({relatedTarget: inner}),
);
});
// @gate experimental
it('is called after many elements are unmounted', () => {
const buttonRef = React.createRef();
const inputRef = React.createRef();
const Component = ({show}) => {
useFocusWithin(ref, {
onBeforeBlurWithin,
onAfterBlurWithin,
});
return (
<div ref={ref}>
{show && <button>Press me!</button>}
{show && <button>Press me!</button>}
{show && <input ref={inputRef} />}
{show && <button>Press me!</button>}
{!show && <button ref={buttonRef}>Press me!</button>}
{show && <button>Press me!</button>}
<button>Press me!</button>
<button>Press me!</button>
</div>
);
};
ReactDOM.render(<Component show={true} />, container);
inputRef.current.focus();
expect(onBeforeBlurWithin).toHaveBeenCalledTimes(0);
expect(onAfterBlurWithin).toHaveBeenCalledTimes(0);
ReactDOM.render(<Component show={false} />, container);
expect(onBeforeBlurWithin).toHaveBeenCalledTimes(1);
expect(onAfterBlurWithin).toHaveBeenCalledTimes(1);
});
// @gate experimental
it('is called after a nested focused element is unmounted (with scope query)', () => {
const TestScope = React.unstable_createScope();
const testScopeQuery = (type, props) => true;
let targetNodes;
let targetNode;
const Component = ({show}) => {
const scopeRef = React.useRef(null);
useFocusWithin(scopeRef, {
onBeforeBlurWithin(event) {
const scope = scopeRef.current;
targetNode = innerRef.current;
targetNodes = scope.DO_NOT_USE_queryAllNodes(testScopeQuery);
},
});
return (
<TestScope ref={scopeRef}>
{show && <input ref={innerRef} />}
</TestScope>
);
};
ReactDOM.render(<Component show={true} />, container);
const inner = innerRef.current;
const target = createEventTarget(inner);
target.keydown({key: 'Tab'});
target.focus();
ReactDOM.render(<Component show={false} />, container);
expect(targetNodes).toEqual([targetNode]);
});
// @gate experimental
it('is called after a focused suspended element is hidden', () => {
const Suspense = React.Suspense;
let suspend = false;
let resolve;
const promise = new Promise(resolvePromise => (resolve = resolvePromise));
function Child() {
if (suspend) {
throw promise;
} else {
return <input ref={innerRef} />;
}
}
const Component = ({show}) => {
useFocusWithin(ref, {
onBeforeBlurWithin,
onAfterBlurWithin,
});
return (
<div ref={ref}>
<Suspense fallback="Loading...">
<Child />
</Suspense>
</div>
);
};
const root = ReactDOM.createRoot(container2);
act(() => {
root.render(<Component />);
});
jest.runAllTimers();
expect(container2.innerHTML).toBe('<div><input></div>');
const inner = innerRef.current;
const target = createEventTarget(inner);
target.keydown({key: 'Tab'});
target.focus();
expect(onBeforeBlurWithin).toHaveBeenCalledTimes(0);
expect(onAfterBlurWithin).toHaveBeenCalledTimes(0);
suspend = true;
act(() => {
root.render(<Component />);
});
jest.runAllTimers();
expect(container2.innerHTML).toBe(
'<div><input style="display: none;">Loading...</div>',
);
expect(onBeforeBlurWithin).toHaveBeenCalledTimes(1);
expect(onAfterBlurWithin).toHaveBeenCalledTimes(1);
resolve();
});
// @gate experimental
it('is called after a focused suspended element is hidden then shown', () => {
const Suspense = React.Suspense;
let suspend = false;
let resolve;
const promise = new Promise(resolvePromise => (resolve = resolvePromise));
const buttonRef = React.createRef();
function Child() {
if (suspend) {
throw promise;
} else {
return <input ref={innerRef} />;
}
}
const Component = ({show}) => {
useFocusWithin(ref, {
onBeforeBlurWithin,
onAfterBlurWithin,
});
return (
<div ref={ref}>
<Suspense fallback={<button ref={buttonRef}>Loading...</button>}>
<Child />
</Suspense>
</div>
);
};
const root = ReactDOM.createRoot(container2);
act(() => {
root.render(<Component />);
});
jest.runAllTimers();
expect(onBeforeBlurWithin).toHaveBeenCalledTimes(0);
expect(onAfterBlurWithin).toHaveBeenCalledTimes(0);
suspend = true;
act(() => {
root.render(<Component />);
});
jest.runAllTimers();
expect(onBeforeBlurWithin).toHaveBeenCalledTimes(0);
expect(onAfterBlurWithin).toHaveBeenCalledTimes(0);
act(() => {
root.render(<Component />);
});
jest.runAllTimers();
expect(onBeforeBlurWithin).toHaveBeenCalledTimes(0);
expect(onAfterBlurWithin).toHaveBeenCalledTimes(0);
buttonRef.current.focus();
suspend = false;
act(() => {
root.render(<Component />);
});
jest.runAllTimers();
expect(onBeforeBlurWithin).toHaveBeenCalledTimes(1);
expect(onAfterBlurWithin).toHaveBeenCalledTimes(1);
resolve();
});
});
});
@@ -77,9 +77,6 @@ export type UpdatePayload = Object;
export type TimeoutHandle = TimeoutID;
export type NoTimeout = -1;
export type ReactListenerEvent = Object;
export type ReactListenerMap = Object;
export type ReactListener = Object;
export type OpaqueIDType = void;
export type RendererInspectionConfig = $ReadOnly<{|
@@ -510,22 +507,6 @@ export function makeServerId(): OpaqueIDType {
throw new Error('Not yet implemented');
}
export function registerEvent(event: any, rootContainerInstance: Container) {
throw new Error('Not yet implemented.');
}
export function mountEventListener(listener: any) {
throw new Error('Not yet implemented.');
}
export function unmountEventListener(listener: any) {
throw new Error('Not yet implemented.');
}
export function validateEventListenerTarget(target: any, listener: any) {
throw new Error('Not yet implemented.');
}
export function beforeActiveInstanceBlur() {
// noop
}
@@ -28,10 +28,6 @@ import ReactNativeFiberHostComponent from './ReactNativeFiberHostComponent';
const {get: getViewConfigForType} = ReactNativeViewConfigRegistry;
export type ReactListenerEvent = Object;
export type ReactListenerMap = Object;
export type ReactListener = Object;
export type Type = string;
export type Props = Object;
export type Container = number;
@@ -559,22 +555,6 @@ export function makeServerId(): OpaqueIDType {
throw new Error('Not yet implemented');
}
export function registerEvent(event: any, rootContainerInstance: Container) {
throw new Error('Not yet implemented.');
}
export function mountEventListener(listener: any) {
throw new Error('Not yet implemented.');
}
export function unmountEventListener(listener: any) {
throw new Error('Not yet implemented.');
}
export function validateEventListenerTarget(target: any, listener: any) {
throw new Error('Not yet implemented.');
}
export function beforeActiveInstanceBlur() {
// noop
}
@@ -36,7 +36,6 @@ import {
enableSuspenseCallback,
enableScopeAPI,
runAllPassiveEffectDestroysBeforeCreates,
enableUseEventAPI,
} from 'shared/ReactFeatureFlags';
import {
FunctionComponent,
@@ -1020,9 +1019,7 @@ function commitUnmount(
if (enableDeprecatedFlareAPI) {
unmountDeprecatedResponderListeners(current);
}
if (enableDeprecatedFlareAPI || enableUseEventAPI) {
beforeRemoveInstance(current.stateNode);
}
beforeRemoveInstance(current.stateNode);
safelyDetachRef(current);
return;
}
@@ -36,7 +36,6 @@ import {
enableSuspenseCallback,
enableScopeAPI,
runAllPassiveEffectDestroysBeforeCreates,
enableUseEventAPI,
} from 'shared/ReactFeatureFlags';
import {
FunctionComponent,
@@ -1020,9 +1019,7 @@ function commitUnmount(
if (enableDeprecatedFlareAPI) {
unmountDeprecatedResponderListeners(current);
}
if (enableDeprecatedFlareAPI || enableUseEventAPI) {
beforeRemoveInstance(current.stateNode);
}
beforeRemoveInstance(current.stateNode);
safelyDetachRef(current);
return;
}
@@ -14,7 +14,6 @@ import type {
ReactEventResponder,
ReactContext,
ReactEventResponderListener,
ReactScopeMethods,
} from 'shared/ReactTypes';
import type {Fiber, Dispatcher} from './ReactInternalTypes';
import type {ExpirationTime} from './ReactFiberExpirationTime.new';
@@ -22,15 +21,10 @@ import type {HookEffectTag} from './ReactHookEffectTags';
import type {SuspenseConfig} from './ReactFiberSuspenseConfig';
import type {ReactPriorityLevel} from './ReactInternalTypes';
import type {FiberRoot} from './ReactInternalTypes';
import type {
OpaqueIDType,
ReactListenerEvent,
ReactListenerMap,
ReactListener,
} from './ReactFiberHostConfig';
import type {OpaqueIDType} from './ReactFiberHostConfig';
import ReactSharedInternals from 'shared/ReactSharedInternals';
import {enableDebugTracing, enableUseEventAPI} from 'shared/ReactFeatureFlags';
import {enableDebugTracing} from 'shared/ReactFeatureFlags';
import {markRootExpiredAtTime} from './ReactFiberRoot.new';
import {
@@ -46,7 +40,6 @@ import {
Passive as PassiveEffect,
} from './ReactSideEffectTags';
import {
NoEffect as NoHookEffect,
HasEffect as HookHasEffect,
Layout as HookLayout,
Passive as HookPassive,
@@ -63,12 +56,6 @@ import {
markUnprocessedUpdateTime,
priorityLevelToLabel,
} from './ReactFiberWorkLoop.new';
import {
registerEvent,
mountEventListener as mountHostEventListener,
unmountEventListener as unmountHostEventListener,
validateEventListenerTarget,
} from './ReactFiberHostConfig';
import invariant from 'shared/invariant';
import getComponentName from 'shared/getComponentName';
@@ -95,7 +82,6 @@ import {
setWorkInProgressVersion,
warnAboutMultipleRenderersDEV,
} from './ReactMutableSource.new';
import {getRootHostContainer} from './ReactFiberHostContext.new';
import {getIsRendering} from './ReactCurrentFiber';
import {logStateUpdateScheduled} from './DebugTracing';
@@ -136,7 +122,6 @@ export type HookType =
| 'useDeferredValue'
| 'useTransition'
| 'useMutableSource'
| 'useEvent'
| 'useOpaqueIdentifier';
let didWarnAboutMismatchedHooksForComponent;
@@ -1760,158 +1745,6 @@ function dispatchAction<S, A>(
}
}
const noOpMount = () => {};
function validateNotInFunctionRender(): boolean {
if (currentlyRenderingFiber === null) {
return true;
}
if (__DEV__) {
console.warn(
'Event listener methods from useEvent() cannot be called during render.' +
' These methods should be called in an effect or event callback outside the render.',
);
}
return false;
}
function createReactListener(
event: ReactListenerEvent,
callback: (SyntheticEvent<EventTarget>) => void,
target: EventTarget | ReactScopeMethods,
destroy: Node => void,
): ReactListener {
return {
callback,
destroy,
event,
target,
};
}
function mountEventListener(event: ReactListenerEvent): ReactListenerMap {
if (enableUseEventAPI) {
const hook = mountWorkInProgressHook();
const listenerMap: Map<
EventTarget | ReactScopeMethods,
ReactListener,
> = new Map();
const rootContainerInstance = getRootHostContainer();
// Register the event to the current root to ensure event
// replaying can pick up the event ahead of time.
registerEvent(event, rootContainerInstance);
const clear = () => {
if (validateNotInFunctionRender()) {
const listeners = Array.from(listenerMap.values());
for (let i = 0; i < listeners.length; i++) {
unmountHostEventListener(listeners[i]);
}
listenerMap.clear();
}
};
const destroy = (target: Node) => {
// We don't need to call detachListenerFromInstance
// here as this method should only ever be called
// from renderers that need to remove the instance
// from the map representing an instance that still
// holds a reference to the listenerMap. This means
// things like "window" listeners on ReactDOM should
// never enter this call path as the the instance in
// those cases would be that of "window", which
// should be handled via an optimized route in the
// renderer, making less overhead here. If we change
// this heuristic we should update this path to make
// sure we call detachListenerFromInstance.
listenerMap.delete(target);
};
const reactListenerMap: ReactListenerMap = {
clear,
setListener(
target: EventTarget | ReactScopeMethods,
callback: ?(SyntheticEvent<EventTarget>) => void,
): void {
if (
validateNotInFunctionRender() &&
validateEventListenerTarget(target, callback)
) {
let listener = listenerMap.get(target);
if (listener === undefined) {
if (callback == null) {
return;
}
listener = createReactListener(event, callback, target, destroy);
listenerMap.set(target, listener);
} else {
if (callback == null) {
listenerMap.delete(target);
unmountHostEventListener(listener);
return;
}
listener.callback = callback;
}
mountHostEventListener(listener);
}
},
};
// In order to clear up upon the hook unmounting,
// we ensure we set the effecrt tag so that we visit
// this effect in the commit phase, so we can handle
// clean-up accordingly.
currentlyRenderingFiber.effectTag |= UpdateEffect;
pushEffect(NoHookEffect, noOpMount, clear, null);
hook.memoizedState = [reactListenerMap, event, clear];
return reactListenerMap;
}
// To make Flow not complain
return (undefined: any);
}
function updateEventListener(event: ReactListenerEvent): ReactListenerMap {
if (enableUseEventAPI) {
const hook = updateWorkInProgressHook();
const [reactListenerMap, memoizedEvent, clear] = hook.memoizedState;
if (__DEV__) {
if (memoizedEvent.type !== event.type) {
console.warn(
'The event type argument passed to the useEvent() hook was different between renders.' +
' The event type is static and should never change between renders.',
);
}
if (memoizedEvent.capture !== event.capture) {
console.warn(
'The "capture" option passed to the useEvent() hook was different between renders.' +
' The "capture" option is static and should never change between renders.',
);
}
if (memoizedEvent.priority !== event.priority) {
console.warn(
'The "priority" option passed to the useEvent() hook was different between renders.' +
' The "priority" option is static and should never change between renders.',
);
}
if (memoizedEvent.passive !== event.passive) {
console.warn(
'The "passive" option passed to the useEvent() hook was different between renders.' +
' The "passive" option is static and should never change between renders.',
);
}
}
// In order to clear up upon the hook unmounting,
// we ensure we set the effecrt tag so that we visit
// this effect in the commit phase, so we can handle
// clean-up accordingly.
currentlyRenderingFiber.effectTag |= UpdateEffect;
pushEffect(NoHookEffect, noOpMount, clear, null);
return reactListenerMap;
}
// To make Flow not complain
return (undefined: any);
}
export const ContextOnlyDispatcher: Dispatcher = {
readContext,
@@ -1929,7 +1762,6 @@ export const ContextOnlyDispatcher: Dispatcher = {
useDeferredValue: throwInvalidHookError,
useTransition: throwInvalidHookError,
useMutableSource: throwInvalidHookError,
useEvent: throwInvalidHookError,
useOpaqueIdentifier: throwInvalidHookError,
};
@@ -1950,7 +1782,6 @@ const HooksDispatcherOnMount: Dispatcher = {
useDeferredValue: mountDeferredValue,
useTransition: mountTransition,
useMutableSource: mountMutableSource,
useEvent: mountEventListener,
useOpaqueIdentifier: mountOpaqueIdentifier,
};
@@ -1971,7 +1802,6 @@ const HooksDispatcherOnUpdate: Dispatcher = {
useDeferredValue: updateDeferredValue,
useTransition: updateTransition,
useMutableSource: updateMutableSource,
useEvent: updateEventListener,
useOpaqueIdentifier: updateOpaqueIdentifier,
};
@@ -1992,7 +1822,6 @@ const HooksDispatcherOnRerender: Dispatcher = {
useDeferredValue: rerenderDeferredValue,
useTransition: rerenderTransition,
useMutableSource: updateMutableSource,
useEvent: updateEventListener,
useOpaqueIdentifier: rerenderOpaqueIdentifier,
};
@@ -2151,11 +1980,6 @@ if (__DEV__) {
mountHookTypesDev();
return mountMutableSource(source, getSnapshot, subscribe);
},
useEvent(event: ReactListenerEvent): ReactListenerMap {
currentHookNameInDev = 'useEvent';
mountHookTypesDev();
return mountEventListener(event);
},
useOpaqueIdentifier(): OpaqueIDType | void {
currentHookNameInDev = 'useOpaqueIdentifier';
mountHookTypesDev();
@@ -2286,11 +2110,6 @@ if (__DEV__) {
updateHookTypesDev();
return mountMutableSource(source, getSnapshot, subscribe);
},
useEvent(event: ReactListenerEvent): ReactListenerMap {
currentHookNameInDev = 'useEvent';
updateHookTypesDev();
return mountEventListener(event);
},
useOpaqueIdentifier(): OpaqueIDType | void {
currentHookNameInDev = 'useOpaqueIdentifier';
updateHookTypesDev();
@@ -2421,11 +2240,6 @@ if (__DEV__) {
updateHookTypesDev();
return updateMutableSource(source, getSnapshot, subscribe);
},
useEvent(event: ReactListenerEvent): ReactListenerMap {
currentHookNameInDev = 'useEvent';
updateHookTypesDev();
return updateEventListener(event);
},
useOpaqueIdentifier(): OpaqueIDType | void {
currentHookNameInDev = 'useOpaqueIdentifier';
updateHookTypesDev();
@@ -2557,11 +2371,6 @@ if (__DEV__) {
updateHookTypesDev();
return updateMutableSource(source, getSnapshot, subscribe);
},
useEvent(event: ReactListenerEvent): ReactListenerMap {
currentHookNameInDev = 'useEvent';
updateHookTypesDev();
return updateEventListener(event);
},
useOpaqueIdentifier(): OpaqueIDType | void {
currentHookNameInDev = 'useOpaqueIdentifier';
updateHookTypesDev();
@@ -2707,12 +2516,6 @@ if (__DEV__) {
mountHookTypesDev();
return mountMutableSource(source, getSnapshot, subscribe);
},
useEvent(event: ReactListenerEvent): ReactListenerMap {
currentHookNameInDev = 'useEvent';
warnInvalidHookAccess();
mountHookTypesDev();
return mountEventListener(event);
},
useOpaqueIdentifier(): OpaqueIDType | void {
currentHookNameInDev = 'useOpaqueIdentifier';
warnInvalidHookAccess();
@@ -2859,12 +2662,6 @@ if (__DEV__) {
updateHookTypesDev();
return updateMutableSource(source, getSnapshot, subscribe);
},
useEvent(event: ReactListenerEvent): ReactListenerMap {
currentHookNameInDev = 'useEvent';
warnInvalidHookAccess();
updateHookTypesDev();
return updateEventListener(event);
},
useOpaqueIdentifier(): OpaqueIDType | void {
currentHookNameInDev = 'useOpaqueIdentifier';
warnInvalidHookAccess();
@@ -3012,12 +2809,6 @@ if (__DEV__) {
updateHookTypesDev();
return updateMutableSource(source, getSnapshot, subscribe);
},
useEvent(event: ReactListenerEvent): ReactListenerMap {
currentHookNameInDev = 'useEvent';
warnInvalidHookAccess();
updateHookTypesDev();
return updateEventListener(event);
},
useOpaqueIdentifier(): OpaqueIDType | void {
currentHookNameInDev = 'useOpaqueIdentifier';
warnInvalidHookAccess();
@@ -14,7 +14,6 @@ import type {
ReactEventResponder,
ReactContext,
ReactEventResponderListener,
ReactScopeMethods,
} from 'shared/ReactTypes';
import type {Fiber, Dispatcher} from './ReactInternalTypes';
import type {ExpirationTime} from './ReactFiberExpirationTime.old';
@@ -22,15 +21,10 @@ import type {HookEffectTag} from './ReactHookEffectTags';
import type {SuspenseConfig} from './ReactFiberSuspenseConfig';
import type {ReactPriorityLevel} from './ReactInternalTypes';
import type {FiberRoot} from './ReactInternalTypes';
import type {
OpaqueIDType,
ReactListenerEvent,
ReactListenerMap,
ReactListener,
} from './ReactFiberHostConfig';
import type {OpaqueIDType} from './ReactFiberHostConfig';
import ReactSharedInternals from 'shared/ReactSharedInternals';
import {enableDebugTracing, enableUseEventAPI} from 'shared/ReactFeatureFlags';
import {enableDebugTracing} from 'shared/ReactFeatureFlags';
import {markRootExpiredAtTime} from './ReactFiberRoot.old';
import {NoMode, BlockingMode, DebugTracingMode} from './ReactTypeOfMode';
@@ -46,7 +40,6 @@ import {
Passive as PassiveEffect,
} from './ReactSideEffectTags';
import {
NoEffect as NoHookEffect,
HasEffect as HookHasEffect,
Layout as HookLayout,
Passive as HookPassive,
@@ -63,12 +56,6 @@ import {
markUnprocessedUpdateTime,
priorityLevelToLabel,
} from './ReactFiberWorkLoop.old';
import {
registerEvent,
mountEventListener as mountHostEventListener,
unmountEventListener as unmountHostEventListener,
validateEventListenerTarget,
} from './ReactFiberHostConfig';
import invariant from 'shared/invariant';
import getComponentName from 'shared/getComponentName';
@@ -95,7 +82,6 @@ import {
setWorkInProgressVersion,
warnAboutMultipleRenderersDEV,
} from './ReactMutableSource.old';
import {getRootHostContainer} from './ReactFiberHostContext.old';
import {getIsRendering} from './ReactCurrentFiber';
import {logStateUpdateScheduled} from './DebugTracing';
@@ -133,7 +119,6 @@ export type HookType =
| 'useDeferredValue'
| 'useTransition'
| 'useMutableSource'
| 'useEvent'
| 'useOpaqueIdentifier';
let didWarnAboutMismatchedHooksForComponent;
@@ -1755,158 +1740,6 @@ function dispatchAction<S, A>(
}
}
const noOpMount = () => {};
function validateNotInFunctionRender(): boolean {
if (currentlyRenderingFiber === null) {
return true;
}
if (__DEV__) {
console.warn(
'Event listener methods from useEvent() cannot be called during render.' +
' These methods should be called in an effect or event callback outside the render.',
);
}
return false;
}
function createReactListener(
event: ReactListenerEvent,
callback: (SyntheticEvent<EventTarget>) => void,
target: EventTarget | ReactScopeMethods,
destroy: Node => void,
): ReactListener {
return {
callback,
destroy,
event,
target,
};
}
function mountEventListener(event: ReactListenerEvent): ReactListenerMap {
if (enableUseEventAPI) {
const hook = mountWorkInProgressHook();
const listenerMap: Map<
EventTarget | ReactScopeMethods,
ReactListener,
> = new Map();
const rootContainerInstance = getRootHostContainer();
// Register the event to the current root to ensure event
// replaying can pick up the event ahead of time.
registerEvent(event, rootContainerInstance);
const clear = () => {
if (validateNotInFunctionRender()) {
const listeners = Array.from(listenerMap.values());
for (let i = 0; i < listeners.length; i++) {
unmountHostEventListener(listeners[i]);
}
listenerMap.clear();
}
};
const destroy = (target: Node) => {
// We don't need to call detachListenerFromInstance
// here as this method should only ever be called
// from renderers that need to remove the instance
// from the map representing an instance that still
// holds a reference to the listenerMap. This means
// things like "window" listeners on ReactDOM should
// never enter this call path as the the instance in
// those cases would be that of "window", which
// should be handled via an optimized route in the
// renderer, making less overhead here. If we change
// this heuristic we should update this path to make
// sure we call detachListenerFromInstance.
listenerMap.delete(target);
};
const reactListenerMap: ReactListenerMap = {
clear,
setListener(
target: EventTarget | ReactScopeMethods,
callback: ?(SyntheticEvent<EventTarget>) => void,
): void {
if (
validateNotInFunctionRender() &&
validateEventListenerTarget(target, callback)
) {
let listener = listenerMap.get(target);
if (listener === undefined) {
if (callback == null) {
return;
}
listener = createReactListener(event, callback, target, destroy);
listenerMap.set(target, listener);
} else {
if (callback == null) {
listenerMap.delete(target);
unmountHostEventListener(listener);
return;
}
listener.callback = callback;
}
mountHostEventListener(listener);
}
},
};
// In order to clear up upon the hook unmounting,
// we ensure we set the effecrt tag so that we visit
// this effect in the commit phase, so we can handle
// clean-up accordingly.
currentlyRenderingFiber.effectTag |= UpdateEffect;
pushEffect(NoHookEffect, noOpMount, clear, null);
hook.memoizedState = [reactListenerMap, event, clear];
return reactListenerMap;
}
// To make Flow not complain
return (undefined: any);
}
function updateEventListener(event: ReactListenerEvent): ReactListenerMap {
if (enableUseEventAPI) {
const hook = updateWorkInProgressHook();
const [reactListenerMap, memoizedEvent, clear] = hook.memoizedState;
if (__DEV__) {
if (memoizedEvent.type !== event.type) {
console.warn(
'The event type argument passed to the useEvent() hook was different between renders.' +
' The event type is static and should never change between renders.',
);
}
if (memoizedEvent.capture !== event.capture) {
console.warn(
'The "capture" option passed to the useEvent() hook was different between renders.' +
' The "capture" option is static and should never change between renders.',
);
}
if (memoizedEvent.priority !== event.priority) {
console.warn(
'The "priority" option passed to the useEvent() hook was different between renders.' +
' The "priority" option is static and should never change between renders.',
);
}
if (memoizedEvent.passive !== event.passive) {
console.warn(
'The "passive" option passed to the useEvent() hook was different between renders.' +
' The "passive" option is static and should never change between renders.',
);
}
}
// In order to clear up upon the hook unmounting,
// we ensure we set the effecrt tag so that we visit
// this effect in the commit phase, so we can handle
// clean-up accordingly.
currentlyRenderingFiber.effectTag |= UpdateEffect;
pushEffect(NoHookEffect, noOpMount, clear, null);
return reactListenerMap;
}
// To make Flow not complain
return (undefined: any);
}
export const ContextOnlyDispatcher: Dispatcher = {
readContext,
@@ -1924,7 +1757,6 @@ export const ContextOnlyDispatcher: Dispatcher = {
useDeferredValue: throwInvalidHookError,
useTransition: throwInvalidHookError,
useMutableSource: throwInvalidHookError,
useEvent: throwInvalidHookError,
useOpaqueIdentifier: throwInvalidHookError,
};
@@ -1945,7 +1777,6 @@ const HooksDispatcherOnMount: Dispatcher = {
useDeferredValue: mountDeferredValue,
useTransition: mountTransition,
useMutableSource: mountMutableSource,
useEvent: mountEventListener,
useOpaqueIdentifier: mountOpaqueIdentifier,
};
@@ -1966,7 +1797,6 @@ const HooksDispatcherOnUpdate: Dispatcher = {
useDeferredValue: updateDeferredValue,
useTransition: updateTransition,
useMutableSource: updateMutableSource,
useEvent: updateEventListener,
useOpaqueIdentifier: updateOpaqueIdentifier,
};
@@ -1987,7 +1817,6 @@ const HooksDispatcherOnRerender: Dispatcher = {
useDeferredValue: rerenderDeferredValue,
useTransition: rerenderTransition,
useMutableSource: updateMutableSource,
useEvent: updateEventListener,
useOpaqueIdentifier: rerenderOpaqueIdentifier,
};
@@ -2146,11 +1975,6 @@ if (__DEV__) {
mountHookTypesDev();
return mountMutableSource(source, getSnapshot, subscribe);
},
useEvent(event: ReactListenerEvent): ReactListenerMap {
currentHookNameInDev = 'useEvent';
mountHookTypesDev();
return mountEventListener(event);
},
useOpaqueIdentifier(): OpaqueIDType | void {
currentHookNameInDev = 'useOpaqueIdentifier';
mountHookTypesDev();
@@ -2281,11 +2105,6 @@ if (__DEV__) {
updateHookTypesDev();
return mountMutableSource(source, getSnapshot, subscribe);
},
useEvent(event: ReactListenerEvent): ReactListenerMap {
currentHookNameInDev = 'useEvent';
updateHookTypesDev();
return mountEventListener(event);
},
useOpaqueIdentifier(): OpaqueIDType | void {
currentHookNameInDev = 'useOpaqueIdentifier';
updateHookTypesDev();
@@ -2416,11 +2235,6 @@ if (__DEV__) {
updateHookTypesDev();
return updateMutableSource(source, getSnapshot, subscribe);
},
useEvent(event: ReactListenerEvent): ReactListenerMap {
currentHookNameInDev = 'useEvent';
updateHookTypesDev();
return updateEventListener(event);
},
useOpaqueIdentifier(): OpaqueIDType | void {
currentHookNameInDev = 'useOpaqueIdentifier';
updateHookTypesDev();
@@ -2552,11 +2366,6 @@ if (__DEV__) {
updateHookTypesDev();
return updateMutableSource(source, getSnapshot, subscribe);
},
useEvent(event: ReactListenerEvent): ReactListenerMap {
currentHookNameInDev = 'useEvent';
updateHookTypesDev();
return updateEventListener(event);
},
useOpaqueIdentifier(): OpaqueIDType | void {
currentHookNameInDev = 'useOpaqueIdentifier';
updateHookTypesDev();
@@ -2702,12 +2511,6 @@ if (__DEV__) {
mountHookTypesDev();
return mountMutableSource(source, getSnapshot, subscribe);
},
useEvent(event: ReactListenerEvent): ReactListenerMap {
currentHookNameInDev = 'useEvent';
warnInvalidHookAccess();
mountHookTypesDev();
return mountEventListener(event);
},
useOpaqueIdentifier(): OpaqueIDType | void {
currentHookNameInDev = 'useOpaqueIdentifier';
warnInvalidHookAccess();
@@ -2854,12 +2657,6 @@ if (__DEV__) {
updateHookTypesDev();
return updateMutableSource(source, getSnapshot, subscribe);
},
useEvent(event: ReactListenerEvent): ReactListenerMap {
currentHookNameInDev = 'useEvent';
warnInvalidHookAccess();
updateHookTypesDev();
return updateEventListener(event);
},
useOpaqueIdentifier(): OpaqueIDType | void {
currentHookNameInDev = 'useOpaqueIdentifier';
warnInvalidHookAccess();
@@ -3007,12 +2804,6 @@ if (__DEV__) {
updateHookTypesDev();
return updateMutableSource(source, getSnapshot, subscribe);
},
useEvent(event: ReactListenerEvent): ReactListenerMap {
currentHookNameInDev = 'useEvent';
warnInvalidHookAccess();
updateHookTypesDev();
return updateEventListener(event);
},
useOpaqueIdentifier(): OpaqueIDType | void {
currentHookNameInDev = 'useOpaqueIdentifier';
warnInvalidHookAccess();
+2 -9
View File
@@ -18,10 +18,7 @@ import type {
MutableSourceGetSnapshotFn,
MutableSource,
} from 'shared/ReactTypes';
import type {
SuspenseInstance,
ReactListenerEvent,
} from './ReactFiberHostConfig';
import type {SuspenseInstance} from './ReactFiberHostConfig';
import type {WorkTag} from './ReactWorkTags';
import type {TypeOfMode} from './ReactTypeOfMode';
import type {SideEffectTag} from './ReactSideEffectTags';
@@ -32,10 +29,7 @@ import type {RootTag} from './ReactRootTags';
import type {TimeoutHandle, NoTimeout} from './ReactFiberHostConfig';
import type {Wakeable} from 'shared/ReactTypes';
import type {Interaction} from 'scheduler/src/Tracing';
import type {
OpaqueIDType,
ReactListenerMap,
} from 'react-reconciler/src/ReactFiberHostConfig';
import type {OpaqueIDType} from 'react-reconciler/src/ReactFiberHostConfig';
import type {SuspenseConfig, TimeoutConfig} from './ReactFiberSuspenseConfig';
export type ReactPriorityLevel = 99 | 98 | 97 | 96 | 95 | 90;
@@ -325,6 +319,5 @@ export type Dispatcher = {|
getSnapshot: MutableSourceGetSnapshotFn<Source, Snapshot>,
subscribe: MutableSourceSubscribeFn<Source, Snapshot>,
): Snapshot,
useEvent(event: ReactListenerEvent): ReactListenerMap,
useOpaqueIdentifier(): OpaqueIDType | void,
|};
@@ -40,9 +40,6 @@ export opaque type NoTimeout = mixed; // eslint-disable-line no-undef
export opaque type RendererInspectionConfig = mixed; // eslint-disable-line no-undef
export opaque type OpaqueIDType = mixed;
export type EventResponder = any;
export type ReactListenerEvent = Object;
export type ReactListenerMap = Object;
export type ReactListener = Object;
export const getPublicInstance = $$$hostConfig.getPublicInstance;
export const getRootHostContext = $$$hostConfig.getRootHostContext;
@@ -78,11 +75,6 @@ export const shouldUpdateFundamentalComponent =
$$$hostConfig.shouldUpdateFundamentalComponent;
export const getInstanceFromNode = $$$hostConfig.getInstanceFromNode;
export const beforeRemoveInstance = $$$hostConfig.beforeRemoveInstance;
export const registerEvent = $$$hostConfig.registerEvent;
export const mountEventListener = $$$hostConfig.mountEventListener;
export const unmountEventListener = $$$hostConfig.unmountEventListener;
export const validateEventListenerTarget =
$$$hostConfig.validateEventListenerTarget;
export const isOpaqueHydratingObject = $$$hostConfig.isOpaqueHydratingObject;
export const makeOpaqueHydratingObject =
$$$hostConfig.makeOpaqueHydratingObject;
-19
View File
@@ -52,9 +52,6 @@ export opaque type OpaqueIDType =
valueOf: () => string | void,
};
export type ReactListenerEvent = Object;
export type ReactListenerMap = Object;
export type ReactListener = Object;
export type RendererInspectionConfig = $ReadOnly<{||}>;
export * from 'react-reconciler/src/ReactFiberHostConfigWithNoPersistence';
@@ -431,22 +428,6 @@ export function makeOpaqueHydratingObject(
};
}
export function registerEvent(event: any, rootContainerInstance: Container) {
throw new Error('Not yet implemented.');
}
export function mountEventListener(listener: any) {
throw new Error('Not yet implemented.');
}
export function unmountEventListener(listener: any) {
throw new Error('Not yet implemented.');
}
export function validateEventListenerTarget(target: any, listener: any) {
throw new Error('Not yet implemented.');
}
export function beforeActiveInstanceBlur() {
// noop
}
-3
View File
@@ -57,9 +57,6 @@ export const enableFundamentalAPI = false;
// Experimental Scope support.
export const enableScopeAPI = false;
// Experimental useEvent support.
export const enableUseEventAPI = false;
// New API for JSX transforms to target - https://github.com/reactjs/rfcs/pull/107
// We will enforce mocking scheduler with scheduler/unstable_mock at some point. (v17?)
@@ -27,7 +27,6 @@ export const warnAboutDeprecatedLifecycles = true;
export const enableDeprecatedFlareAPI = false;
export const enableFundamentalAPI = false;
export const enableScopeAPI = false;
export const enableUseEventAPI = false;
export const warnAboutUnmockedScheduler = true;
export const enableSuspenseCallback = false;
export const warnAboutDefaultPropsOnFunctionComponents = false;
@@ -26,7 +26,6 @@ export const enableSchedulerDebugging = false;
export const enableDeprecatedFlareAPI = false;
export const enableFundamentalAPI = false;
export const enableScopeAPI = false;
export const enableUseEventAPI = false;
export const warnAboutUnmockedScheduler = false;
export const enableSuspenseCallback = false;
export const warnAboutDefaultPropsOnFunctionComponents = false;
@@ -26,7 +26,6 @@ export const enableSchedulerDebugging = false;
export const enableDeprecatedFlareAPI = false;
export const enableFundamentalAPI = false;
export const enableScopeAPI = false;
export const enableUseEventAPI = false;
export const warnAboutUnmockedScheduler = false;
export const enableSuspenseCallback = false;
export const warnAboutDefaultPropsOnFunctionComponents = false;
@@ -26,7 +26,6 @@ export const disableInputAttributeSyncing = false;
export const enableDeprecatedFlareAPI = true;
export const enableFundamentalAPI = false;
export const enableScopeAPI = true;
export const enableUseEventAPI = true;
export const warnAboutUnmockedScheduler = true;
export const enableSuspenseCallback = true;
export const warnAboutDefaultPropsOnFunctionComponents = false;
@@ -26,7 +26,6 @@ export const enableSchedulerDebugging = false;
export const enableDeprecatedFlareAPI = false;
export const enableFundamentalAPI = false;
export const enableScopeAPI = false;
export const enableUseEventAPI = false;
export const warnAboutUnmockedScheduler = false;
export const enableSuspenseCallback = false;
export const warnAboutDefaultPropsOnFunctionComponents = false;
@@ -26,7 +26,6 @@ export const enableSchedulerDebugging = false;
export const enableDeprecatedFlareAPI = true;
export const enableFundamentalAPI = false;
export const enableScopeAPI = true;
export const enableUseEventAPI = true;
export const warnAboutUnmockedScheduler = true;
export const enableSuspenseCallback = true;
export const warnAboutDefaultPropsOnFunctionComponents = false;
@@ -60,8 +60,6 @@ export const enableFundamentalAPI = false;
export const enableScopeAPI = true;
export const enableUseEventAPI = true;
export const warnAboutUnmockedScheduler = true;
export const enableSuspenseCallback = true;