Skip to content

Commit 371bbf3

Browse files
authored
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
1 parent ab5fe17 commit 371bbf3

11 files changed

+240
-101
lines changed

packages/events/EventSystemFlags.js

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow
8+
*/
9+
10+
export type EventSystemFlags = number;
11+
12+
export const PLUGIN_EVENT_SYSTEM = 1;
13+
export const RESPONDER_EVENT_SYSTEM = 1 << 1;
14+
export const IS_PASSIVE = 1 << 2;
15+
export const IS_ACTIVE = 1 << 3;
16+
export const PASSIVE_NOT_SUPPORTED = 1 << 4;

packages/events/ReactGenericBatching.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ import {
2020
let _batchedUpdatesImpl = function(fn, bookkeeping) {
2121
return fn(bookkeeping);
2222
};
23-
let _interactiveUpdatesImpl = function(fn, a, b) {
24-
return fn(a, b);
23+
let _interactiveUpdatesImpl = function(fn, a, b, c) {
24+
return fn(a, b, c);
2525
};
2626
let _flushInteractiveUpdatesImpl = function() {};
2727

@@ -52,8 +52,8 @@ export function batchedUpdates(fn, bookkeeping) {
5252
}
5353
}
5454

55-
export function interactiveUpdates(fn, a, b) {
56-
return _interactiveUpdatesImpl(fn, a, b);
55+
export function interactiveUpdates(fn, a, b, c) {
56+
return _interactiveUpdatesImpl(fn, a, b, c);
5757
}
5858

5959
export function flushInteractiveUpdates() {

packages/react-dom/src/client/ReactDOMComponent.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,10 @@ if (__DEV__) {
253253
};
254254
}
255255

256-
function ensureListeningTo(rootContainerElement, registrationName) {
256+
function ensureListeningTo(
257+
rootContainerElement: Element | Node,
258+
registrationName: string,
259+
): void {
257260
const isDocumentOrFragment =
258261
rootContainerElement.nodeType === DOCUMENT_NODE ||
259262
rootContainerElement.nodeType === DOCUMENT_FRAGMENT_NODE;

packages/react-dom/src/events/EventListener.js

+11-2
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,26 @@
88
*/
99

1010
export function addEventBubbleListener(
11-
element: Document | Element,
11+
element: Document | Element | Node,
1212
eventType: string,
1313
listener: Function,
1414
): void {
1515
element.addEventListener(eventType, listener, false);
1616
}
1717

1818
export function addEventCaptureListener(
19-
element: Document | Element,
19+
element: Document | Element | Node,
2020
eventType: string,
2121
listener: Function,
2222
): void {
2323
element.addEventListener(eventType, listener, true);
2424
}
25+
26+
export function addEventListener(
27+
element: Document | Element | Node,
28+
eventType: string,
29+
listener: Function,
30+
options: {passive: boolean},
31+
): void {
32+
element.addEventListener(eventType, listener, (options: any));
33+
}

packages/react-dom/src/events/ReactBrowserEventEmitter.js

+27-24
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
*/
99

1010
import {registrationNameDependencies} from 'events/EventPluginRegistry';
11+
import type {DOMTopLevelEventType} from 'events/TopLevelEventTypes';
1112
import {
1213
TOP_BLUR,
1314
TOP_CANCEL,
@@ -84,22 +85,23 @@ import isEventSupported from './isEventSupported';
8485
* React Core . General Purpose Event Plugin System
8586
*/
8687

87-
const alreadyListeningTo = {};
88-
let reactTopListenersCounter = 0;
88+
const PossiblyWeakMap = typeof WeakMap === 'function' ? WeakMap : Map;
89+
const elementListeningSets:
90+
| WeakMap
91+
| Map<
92+
Document | Element | Node,
93+
Set<DOMTopLevelEventType>,
94+
> = new PossiblyWeakMap();
8995

90-
/**
91-
* To ensure no conflicts with other potential React instances on the page
92-
*/
93-
const topListenersIDKey = '_reactListenersID' + ('' + Math.random()).slice(2);
94-
95-
function getListeningForDocument(mountAt: any) {
96-
// In IE8, `mountAt` is a host object and doesn't have `hasOwnProperty`
97-
// directly.
98-
if (!Object.prototype.hasOwnProperty.call(mountAt, topListenersIDKey)) {
99-
mountAt[topListenersIDKey] = reactTopListenersCounter++;
100-
alreadyListeningTo[mountAt[topListenersIDKey]] = {};
96+
function getListeningSetForElement(
97+
element: Document | Element | Node,
98+
): Set<DOMTopLevelEventType> {
99+
let listeningSet = elementListeningSets.get(element);
100+
if (listeningSet === undefined) {
101+
listeningSet = new Set();
102+
elementListeningSets.set(element, listeningSet);
101103
}
102-
return alreadyListeningTo[mountAt[topListenersIDKey]];
104+
return listeningSet;
103105
}
104106

105107
/**
@@ -125,14 +127,14 @@ function getListeningForDocument(mountAt: any) {
125127
*/
126128
export function listenTo(
127129
registrationName: string,
128-
mountAt: Document | Element,
129-
) {
130-
const isListening = getListeningForDocument(mountAt);
130+
mountAt: Document | Element | Node,
131+
): void {
132+
const listeningSet = getListeningSetForElement(mountAt);
131133
const dependencies = registrationNameDependencies[registrationName];
132134

133135
for (let i = 0; i < dependencies.length; i++) {
134136
const dependency = dependencies[i];
135-
if (!(isListening.hasOwnProperty(dependency) && isListening[dependency])) {
137+
if (!listeningSet.has(dependency)) {
136138
switch (dependency) {
137139
case TOP_SCROLL:
138140
trapCapturedEvent(TOP_SCROLL, mountAt);
@@ -143,8 +145,8 @@ export function listenTo(
143145
trapCapturedEvent(TOP_BLUR, mountAt);
144146
// We set the flag for a single dependency later in this function,
145147
// but this ensures we mark both as attached rather than just one.
146-
isListening[TOP_BLUR] = true;
147-
isListening[TOP_FOCUS] = true;
148+
listeningSet.add(TOP_BLUR);
149+
listeningSet.add(TOP_FOCUS);
148150
break;
149151
case TOP_CANCEL:
150152
case TOP_CLOSE:
@@ -167,20 +169,21 @@ export function listenTo(
167169
}
168170
break;
169171
}
170-
isListening[dependency] = true;
172+
listeningSet.add(dependency);
171173
}
172174
}
173175
}
174176

175177
export function isListeningToAllDependencies(
176178
registrationName: string,
177179
mountAt: Document | Element,
178-
) {
179-
const isListening = getListeningForDocument(mountAt);
180+
): boolean {
181+
const listeningSet = getListeningSetForElement(mountAt);
180182
const dependencies = registrationNameDependencies[registrationName];
183+
181184
for (let i = 0; i < dependencies.length; i++) {
182185
const dependency = dependencies[i];
183-
if (!(isListening.hasOwnProperty(dependency) && isListening[dependency])) {
186+
if (!listeningSet.has(dependency)) {
184187
return false;
185188
}
186189
}

0 commit comments

Comments
 (0)