mirror of
https://github.com/zebrajr/react.git
synced 2026-01-15 12:15:22 +00:00
Add infrastructure for passive/non-passive event support for future API exploration (#15036)
* Add infrastructure for passive/non-passive event support for future event API experimentation
This commit is contained in:
16
packages/events/EventSystemFlags.js
Normal file
16
packages/events/EventSystemFlags.js
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* 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 type EventSystemFlags = number;
|
||||
|
||||
export const PLUGIN_EVENT_SYSTEM = 1;
|
||||
export const RESPONDER_EVENT_SYSTEM = 1 << 1;
|
||||
export const IS_PASSIVE = 1 << 2;
|
||||
export const IS_ACTIVE = 1 << 3;
|
||||
export const PASSIVE_NOT_SUPPORTED = 1 << 4;
|
||||
@@ -20,8 +20,8 @@ import {
|
||||
let _batchedUpdatesImpl = function(fn, bookkeeping) {
|
||||
return fn(bookkeeping);
|
||||
};
|
||||
let _interactiveUpdatesImpl = function(fn, a, b) {
|
||||
return fn(a, b);
|
||||
let _interactiveUpdatesImpl = function(fn, a, b, c) {
|
||||
return fn(a, b, c);
|
||||
};
|
||||
let _flushInteractiveUpdatesImpl = function() {};
|
||||
|
||||
@@ -52,8 +52,8 @@ export function batchedUpdates(fn, bookkeeping) {
|
||||
}
|
||||
}
|
||||
|
||||
export function interactiveUpdates(fn, a, b) {
|
||||
return _interactiveUpdatesImpl(fn, a, b);
|
||||
export function interactiveUpdates(fn, a, b, c) {
|
||||
return _interactiveUpdatesImpl(fn, a, b, c);
|
||||
}
|
||||
|
||||
export function flushInteractiveUpdates() {
|
||||
|
||||
@@ -253,7 +253,10 @@ if (__DEV__) {
|
||||
};
|
||||
}
|
||||
|
||||
function ensureListeningTo(rootContainerElement, registrationName) {
|
||||
function ensureListeningTo(
|
||||
rootContainerElement: Element | Node,
|
||||
registrationName: string,
|
||||
): void {
|
||||
const isDocumentOrFragment =
|
||||
rootContainerElement.nodeType === DOCUMENT_NODE ||
|
||||
rootContainerElement.nodeType === DOCUMENT_FRAGMENT_NODE;
|
||||
|
||||
13
packages/react-dom/src/events/EventListener.js
vendored
13
packages/react-dom/src/events/EventListener.js
vendored
@@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
export function addEventBubbleListener(
|
||||
element: Document | Element,
|
||||
element: Document | Element | Node,
|
||||
eventType: string,
|
||||
listener: Function,
|
||||
): void {
|
||||
@@ -16,9 +16,18 @@ export function addEventBubbleListener(
|
||||
}
|
||||
|
||||
export function addEventCaptureListener(
|
||||
element: Document | Element,
|
||||
element: Document | Element | Node,
|
||||
eventType: string,
|
||||
listener: Function,
|
||||
): void {
|
||||
element.addEventListener(eventType, listener, true);
|
||||
}
|
||||
|
||||
export function addEventListener(
|
||||
element: Document | Element | Node,
|
||||
eventType: string,
|
||||
listener: Function,
|
||||
options: {passive: boolean},
|
||||
): void {
|
||||
element.addEventListener(eventType, listener, (options: any));
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
*/
|
||||
|
||||
import {registrationNameDependencies} from 'events/EventPluginRegistry';
|
||||
import type {DOMTopLevelEventType} from 'events/TopLevelEventTypes';
|
||||
import {
|
||||
TOP_BLUR,
|
||||
TOP_CANCEL,
|
||||
@@ -84,22 +85,23 @@ import isEventSupported from './isEventSupported';
|
||||
* React Core . General Purpose Event Plugin System
|
||||
*/
|
||||
|
||||
const alreadyListeningTo = {};
|
||||
let reactTopListenersCounter = 0;
|
||||
const PossiblyWeakMap = typeof WeakMap === 'function' ? WeakMap : Map;
|
||||
const elementListeningSets:
|
||||
| WeakMap
|
||||
| Map<
|
||||
Document | Element | Node,
|
||||
Set<DOMTopLevelEventType>,
|
||||
> = new PossiblyWeakMap();
|
||||
|
||||
/**
|
||||
* To ensure no conflicts with other potential React instances on the page
|
||||
*/
|
||||
const topListenersIDKey = '_reactListenersID' + ('' + Math.random()).slice(2);
|
||||
|
||||
function getListeningForDocument(mountAt: any) {
|
||||
// In IE8, `mountAt` is a host object and doesn't have `hasOwnProperty`
|
||||
// directly.
|
||||
if (!Object.prototype.hasOwnProperty.call(mountAt, topListenersIDKey)) {
|
||||
mountAt[topListenersIDKey] = reactTopListenersCounter++;
|
||||
alreadyListeningTo[mountAt[topListenersIDKey]] = {};
|
||||
function getListeningSetForElement(
|
||||
element: Document | Element | Node,
|
||||
): Set<DOMTopLevelEventType> {
|
||||
let listeningSet = elementListeningSets.get(element);
|
||||
if (listeningSet === undefined) {
|
||||
listeningSet = new Set();
|
||||
elementListeningSets.set(element, listeningSet);
|
||||
}
|
||||
return alreadyListeningTo[mountAt[topListenersIDKey]];
|
||||
return listeningSet;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -125,14 +127,14 @@ function getListeningForDocument(mountAt: any) {
|
||||
*/
|
||||
export function listenTo(
|
||||
registrationName: string,
|
||||
mountAt: Document | Element,
|
||||
) {
|
||||
const isListening = getListeningForDocument(mountAt);
|
||||
mountAt: Document | Element | Node,
|
||||
): void {
|
||||
const listeningSet = getListeningSetForElement(mountAt);
|
||||
const dependencies = registrationNameDependencies[registrationName];
|
||||
|
||||
for (let i = 0; i < dependencies.length; i++) {
|
||||
const dependency = dependencies[i];
|
||||
if (!(isListening.hasOwnProperty(dependency) && isListening[dependency])) {
|
||||
if (!listeningSet.has(dependency)) {
|
||||
switch (dependency) {
|
||||
case TOP_SCROLL:
|
||||
trapCapturedEvent(TOP_SCROLL, mountAt);
|
||||
@@ -143,8 +145,8 @@ export function listenTo(
|
||||
trapCapturedEvent(TOP_BLUR, mountAt);
|
||||
// We set the flag for a single dependency later in this function,
|
||||
// but this ensures we mark both as attached rather than just one.
|
||||
isListening[TOP_BLUR] = true;
|
||||
isListening[TOP_FOCUS] = true;
|
||||
listeningSet.add(TOP_BLUR);
|
||||
listeningSet.add(TOP_FOCUS);
|
||||
break;
|
||||
case TOP_CANCEL:
|
||||
case TOP_CLOSE:
|
||||
@@ -167,7 +169,7 @@ export function listenTo(
|
||||
}
|
||||
break;
|
||||
}
|
||||
isListening[dependency] = true;
|
||||
listeningSet.add(dependency);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -175,12 +177,13 @@ export function listenTo(
|
||||
export function isListeningToAllDependencies(
|
||||
registrationName: string,
|
||||
mountAt: Document | Element,
|
||||
) {
|
||||
const isListening = getListeningForDocument(mountAt);
|
||||
): boolean {
|
||||
const listeningSet = getListeningSetForElement(mountAt);
|
||||
const dependencies = registrationNameDependencies[registrationName];
|
||||
|
||||
for (let i = 0; i < dependencies.length; i++) {
|
||||
const dependency = dependencies[i];
|
||||
if (!(isListening.hasOwnProperty(dependency) && isListening[dependency])) {
|
||||
if (!listeningSet.has(dependency)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,18 +15,41 @@ import {batchedUpdates, interactiveUpdates} from 'events/ReactGenericBatching';
|
||||
import {runExtractedEventsInBatch} from 'events/EventPluginHub';
|
||||
import {isFiberMounted} from 'react-reconciler/reflection';
|
||||
import {HostRoot} from 'shared/ReactWorkTags';
|
||||
import {
|
||||
type EventSystemFlags,
|
||||
PLUGIN_EVENT_SYSTEM,
|
||||
RESPONDER_EVENT_SYSTEM,
|
||||
IS_PASSIVE,
|
||||
IS_ACTIVE,
|
||||
PASSIVE_NOT_SUPPORTED,
|
||||
} from 'events/EventSystemFlags';
|
||||
|
||||
import {addEventBubbleListener, addEventCaptureListener} from './EventListener';
|
||||
import {
|
||||
addEventBubbleListener,
|
||||
addEventCaptureListener,
|
||||
addEventListener,
|
||||
} from './EventListener';
|
||||
import getEventTarget from './getEventTarget';
|
||||
import {getClosestInstanceFromNode} from '../client/ReactDOMComponentTree';
|
||||
import SimpleEventPlugin from './SimpleEventPlugin';
|
||||
import {getRawEventName} from './DOMTopLevelEventTypes';
|
||||
import {passiveBrowserEventsSupported} from './checkPassiveEvents';
|
||||
|
||||
import {enableEventAPI} from 'shared/ReactFeatureFlags';
|
||||
|
||||
const {isInteractiveTopLevelEventType} = SimpleEventPlugin;
|
||||
|
||||
const CALLBACK_BOOKKEEPING_POOL_SIZE = 10;
|
||||
const callbackBookkeepingPool = [];
|
||||
|
||||
type BookKeepingInstance = {
|
||||
topLevelType: DOMTopLevelEventType | null,
|
||||
nativeEvent: AnyNativeEvent | null,
|
||||
targetInst: Fiber | null,
|
||||
ancestors: Array<Fiber | null>,
|
||||
eventSystemFlags: EventSystemFlags,
|
||||
};
|
||||
|
||||
/**
|
||||
* Find the deepest React component completely containing the root of the
|
||||
* passed-in instance (for use when entire React trees are nested within each
|
||||
@@ -48,20 +71,17 @@ function findRootContainerNode(inst) {
|
||||
|
||||
// Used to store ancestor hierarchy in top level callback
|
||||
function getTopLevelCallbackBookKeeping(
|
||||
topLevelType,
|
||||
nativeEvent,
|
||||
targetInst,
|
||||
): {
|
||||
topLevelType: ?DOMTopLevelEventType,
|
||||
nativeEvent: ?AnyNativeEvent,
|
||||
topLevelType: DOMTopLevelEventType,
|
||||
nativeEvent: AnyNativeEvent,
|
||||
targetInst: Fiber | null,
|
||||
ancestors: Array<Fiber>,
|
||||
} {
|
||||
eventSystemFlags: EventSystemFlags,
|
||||
): BookKeepingInstance {
|
||||
if (callbackBookkeepingPool.length) {
|
||||
const instance = callbackBookkeepingPool.pop();
|
||||
instance.topLevelType = topLevelType;
|
||||
instance.nativeEvent = nativeEvent;
|
||||
instance.targetInst = targetInst;
|
||||
instance.eventSystemFlags = eventSystemFlags;
|
||||
return instance;
|
||||
}
|
||||
return {
|
||||
@@ -69,20 +89,24 @@ function getTopLevelCallbackBookKeeping(
|
||||
nativeEvent,
|
||||
targetInst,
|
||||
ancestors: [],
|
||||
eventSystemFlags,
|
||||
};
|
||||
}
|
||||
|
||||
function releaseTopLevelCallbackBookKeeping(instance) {
|
||||
function releaseTopLevelCallbackBookKeeping(
|
||||
instance: BookKeepingInstance,
|
||||
): void {
|
||||
instance.topLevelType = null;
|
||||
instance.nativeEvent = null;
|
||||
instance.targetInst = null;
|
||||
instance.ancestors.length = 0;
|
||||
instance.eventSystemFlags = 0;
|
||||
if (callbackBookkeepingPool.length < CALLBACK_BOOKKEEPING_POOL_SIZE) {
|
||||
callbackBookkeepingPool.push(instance);
|
||||
}
|
||||
}
|
||||
|
||||
function handleTopLevel(bookKeeping) {
|
||||
function handleTopLevel(bookKeeping: BookKeepingInstance) {
|
||||
let targetInst = bookKeeping.targetInst;
|
||||
|
||||
// Loop through the hierarchy, in case there's any nested components.
|
||||
@@ -92,7 +116,8 @@ function handleTopLevel(bookKeeping) {
|
||||
let ancestor = targetInst;
|
||||
do {
|
||||
if (!ancestor) {
|
||||
bookKeeping.ancestors.push(ancestor);
|
||||
const ancestors = bookKeeping.ancestors;
|
||||
((ancestors: any): Array<Fiber | null>).push(ancestor);
|
||||
break;
|
||||
}
|
||||
const root = findRootContainerNode(ancestor);
|
||||
@@ -105,12 +130,17 @@ function handleTopLevel(bookKeeping) {
|
||||
|
||||
for (let i = 0; i < bookKeeping.ancestors.length; i++) {
|
||||
targetInst = bookKeeping.ancestors[i];
|
||||
runExtractedEventsInBatch(
|
||||
bookKeeping.topLevelType,
|
||||
targetInst,
|
||||
bookKeeping.nativeEvent,
|
||||
getEventTarget(bookKeeping.nativeEvent),
|
||||
);
|
||||
if (bookKeeping.eventSystemFlags === PLUGIN_EVENT_SYSTEM) {
|
||||
runExtractedEventsInBatch(
|
||||
((bookKeeping.topLevelType: any): DOMTopLevelEventType),
|
||||
targetInst,
|
||||
((bookKeeping.nativeEvent: any): AnyNativeEvent),
|
||||
getEventTarget(bookKeeping.nativeEvent),
|
||||
);
|
||||
} else {
|
||||
// RESPONDER_EVENT_SYSTEM
|
||||
// TODO: Add implementation
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,70 +155,89 @@ export function isEnabled() {
|
||||
return _enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Traps top-level events by using event bubbling.
|
||||
*
|
||||
* @param {number} topLevelType Number from `TopLevelEventTypes`.
|
||||
* @param {object} element Element on which to attach listener.
|
||||
* @return {?object} An object with a remove function which will forcefully
|
||||
* remove the listener.
|
||||
* @internal
|
||||
*/
|
||||
export function trapBubbledEvent(
|
||||
topLevelType: DOMTopLevelEventType,
|
||||
element: Document | Element,
|
||||
) {
|
||||
if (!element) {
|
||||
return null;
|
||||
}
|
||||
const dispatch = isInteractiveTopLevelEventType(topLevelType)
|
||||
? dispatchInteractiveEvent
|
||||
: dispatchEvent;
|
||||
|
||||
addEventBubbleListener(
|
||||
element,
|
||||
getRawEventName(topLevelType),
|
||||
// Check if interactive and wrap in interactiveUpdates
|
||||
dispatch.bind(null, topLevelType),
|
||||
);
|
||||
element: Document | Element | Node,
|
||||
): void {
|
||||
trapEventForPluginEventSystem(element, topLevelType, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Traps a top-level event by using event capturing.
|
||||
*
|
||||
* @param {number} topLevelType Number from `TopLevelEventTypes`.
|
||||
* @param {object} element Element on which to attach listener.
|
||||
* @return {?object} An object with a remove function which will forcefully
|
||||
* remove the listener.
|
||||
* @internal
|
||||
*/
|
||||
export function trapCapturedEvent(
|
||||
topLevelType: DOMTopLevelEventType,
|
||||
element: Document | Element,
|
||||
) {
|
||||
if (!element) {
|
||||
return null;
|
||||
element: Document | Element | Node,
|
||||
): void {
|
||||
trapEventForPluginEventSystem(element, topLevelType, true);
|
||||
}
|
||||
|
||||
export function trapEventForResponderEventSystem(
|
||||
element: Document | Element | Node,
|
||||
topLevelType: DOMTopLevelEventType,
|
||||
capture: boolean,
|
||||
passive: boolean,
|
||||
): void {
|
||||
if (enableEventAPI) {
|
||||
const dispatch = isInteractiveTopLevelEventType(topLevelType)
|
||||
? dispatchInteractiveEvent
|
||||
: dispatchEvent;
|
||||
const rawEventName = getRawEventName(topLevelType);
|
||||
let eventFlags = RESPONDER_EVENT_SYSTEM;
|
||||
|
||||
// If passive option is not supported, then the event will be
|
||||
// active and not passive, but we flag it as using not being
|
||||
// supported too. This way the responder event plugins know,
|
||||
// and can provide polyfills if needed.
|
||||
if (passive) {
|
||||
if (passiveBrowserEventsSupported) {
|
||||
eventFlags |= IS_ACTIVE;
|
||||
eventFlags |= PASSIVE_NOT_SUPPORTED;
|
||||
passive = false;
|
||||
} else {
|
||||
eventFlags |= IS_PASSIVE;
|
||||
}
|
||||
} else {
|
||||
eventFlags |= IS_ACTIVE;
|
||||
}
|
||||
// Check if interactive and wrap in interactiveUpdates
|
||||
const listener = dispatch.bind(null, topLevelType, eventFlags);
|
||||
addEventListener(element, rawEventName, listener, {
|
||||
capture,
|
||||
passive,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function trapEventForPluginEventSystem(
|
||||
element: Document | Element | Node,
|
||||
topLevelType: DOMTopLevelEventType,
|
||||
capture: boolean,
|
||||
): void {
|
||||
const dispatch = isInteractiveTopLevelEventType(topLevelType)
|
||||
? dispatchInteractiveEvent
|
||||
: dispatchEvent;
|
||||
|
||||
addEventCaptureListener(
|
||||
element,
|
||||
getRawEventName(topLevelType),
|
||||
// Check if interactive and wrap in interactiveUpdates
|
||||
dispatch.bind(null, topLevelType),
|
||||
);
|
||||
const rawEventName = getRawEventName(topLevelType);
|
||||
// Check if interactive and wrap in interactiveUpdates
|
||||
const listener = dispatch.bind(null, topLevelType, PLUGIN_EVENT_SYSTEM);
|
||||
if (capture) {
|
||||
addEventCaptureListener(element, rawEventName, listener);
|
||||
} else {
|
||||
addEventBubbleListener(element, rawEventName, listener);
|
||||
}
|
||||
}
|
||||
|
||||
function dispatchInteractiveEvent(topLevelType, nativeEvent) {
|
||||
interactiveUpdates(dispatchEvent, topLevelType, nativeEvent);
|
||||
function dispatchInteractiveEvent(topLevelType, eventSystemFlags, nativeEvent) {
|
||||
interactiveUpdates(
|
||||
dispatchEvent,
|
||||
topLevelType,
|
||||
eventSystemFlags,
|
||||
nativeEvent,
|
||||
);
|
||||
}
|
||||
|
||||
export function dispatchEvent(
|
||||
topLevelType: DOMTopLevelEventType,
|
||||
eventSystemFlags: EventSystemFlags,
|
||||
nativeEvent: AnyNativeEvent,
|
||||
) {
|
||||
): void {
|
||||
if (!_enabled) {
|
||||
return;
|
||||
}
|
||||
@@ -211,6 +260,7 @@ export function dispatchEvent(
|
||||
topLevelType,
|
||||
nativeEvent,
|
||||
targetInst,
|
||||
eventSystemFlags,
|
||||
);
|
||||
|
||||
try {
|
||||
|
||||
29
packages/react-dom/src/events/checkPassiveEvents.js
vendored
Normal file
29
packages/react-dom/src/events/checkPassiveEvents.js
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* 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 {canUseDOM} from 'shared/ExecutionEnvironment';
|
||||
import {enableEventAPI} from 'shared/ReactFeatureFlags';
|
||||
|
||||
export 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 (enableEventAPI && canUseDOM) {
|
||||
try {
|
||||
const options = {
|
||||
get passive() {
|
||||
passiveBrowserEventsSupported = true;
|
||||
},
|
||||
};
|
||||
window.addEventListener('test', options, options);
|
||||
window.removeEventListener('test', options, options);
|
||||
} catch (e) {
|
||||
passiveBrowserEventsSupported = false;
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,8 @@ const EventListenerWWW = require('EventListener');
|
||||
import typeof * as EventListenerType from '../EventListener';
|
||||
import typeof * as EventListenerShimType from './EventListener-www';
|
||||
|
||||
const NORMAL_PRIORITY = 0;
|
||||
|
||||
export function addEventBubbleListener(
|
||||
element: Element,
|
||||
eventType: string,
|
||||
@@ -28,6 +30,21 @@ export function addEventCaptureListener(
|
||||
EventListenerWWW.capture(element, eventType, listener);
|
||||
}
|
||||
|
||||
export function addEventListener(
|
||||
element: Element,
|
||||
eventType: string,
|
||||
listener: Function,
|
||||
options: {passive: boolean},
|
||||
): void {
|
||||
EventListenerWWW.listen(
|
||||
element,
|
||||
eventType,
|
||||
listener,
|
||||
NORMAL_PRIORITY,
|
||||
options,
|
||||
);
|
||||
}
|
||||
|
||||
// Flow magic to verify the exports of this file match the original version.
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
type Check<_X, Y: _X, X: Y = _X> = null;
|
||||
|
||||
@@ -21,6 +21,7 @@ import lowPriorityWarning from 'shared/lowPriorityWarning';
|
||||
import warningWithoutStack from 'shared/warningWithoutStack';
|
||||
import {ELEMENT_NODE} from '../shared/HTMLNodeType';
|
||||
import * as DOMTopLevelEventTypes from '../events/DOMTopLevelEventTypes';
|
||||
import {PLUGIN_EVENT_SYSTEM} from 'events/EventSystemFlags';
|
||||
|
||||
// for .act's return value
|
||||
type Thenable = {
|
||||
@@ -63,7 +64,7 @@ let hasWarnedAboutDeprecatedMockComponent = false;
|
||||
*/
|
||||
function simulateNativeEventOnNode(topLevelType, node, fakeNativeEvent) {
|
||||
fakeNativeEvent.target = node;
|
||||
dispatchEvent(topLevelType, fakeNativeEvent);
|
||||
dispatchEvent(topLevelType, PLUGIN_EVENT_SYSTEM, fakeNativeEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2523,9 +2523,14 @@ function flushSync<A, R>(fn: (a: A) => R, a: A): R {
|
||||
}
|
||||
}
|
||||
|
||||
function interactiveUpdates<A, B, R>(fn: (A, B) => R, a: A, b: B): R {
|
||||
function interactiveUpdates<A, B, C, R>(
|
||||
fn: (A, B, C) => R,
|
||||
a: A,
|
||||
b: B,
|
||||
c: C,
|
||||
): R {
|
||||
if (isBatchingInteractiveUpdates) {
|
||||
return fn(a, b);
|
||||
return fn(a, b, c);
|
||||
}
|
||||
// If there are any pending interactive updates, synchronously flush them.
|
||||
// This needs to happen before we read any handlers, because the effect of
|
||||
@@ -2545,7 +2550,7 @@ function interactiveUpdates<A, B, R>(fn: (A, B) => R, a: A, b: B): R {
|
||||
isBatchingInteractiveUpdates = true;
|
||||
isBatchingUpdates = true;
|
||||
try {
|
||||
return fn(a, b);
|
||||
return fn(a, b, c);
|
||||
} finally {
|
||||
isBatchingInteractiveUpdates = previousIsBatchingInteractiveUpdates;
|
||||
isBatchingUpdates = previousIsBatchingUpdates;
|
||||
|
||||
@@ -31,7 +31,13 @@ declare module 'ReactFiberErrorDialog' {
|
||||
// EventListener www fork
|
||||
declare module 'EventListener' {
|
||||
declare module.exports: {
|
||||
listen: (target: Element, type: string, callback: Function) => mixed,
|
||||
listen: (
|
||||
target: Element,
|
||||
type: string,
|
||||
callback: Function,
|
||||
priority?: number,
|
||||
options?: {passive: boolean},
|
||||
) => mixed,
|
||||
capture: (target: Element, type: string, callback: Function) => mixed,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user