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

Modern Event System: refine flags and handling of enableLegacyFBSupport (#18466)

Dominic Gannaway committed Apr 2, 2020 at 18:26 UTC d5e4b3ae1d5f7d76bb3894e8d814fd4ba19dcfd5
9 files changed +112 -39
packages/legacy-events/EventSystemFlags.js
+6 -6
@@ -11,10 +11,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;
17 -export const IS_REPLAYED = 1 << 5;
18 -export const IS_FIRST_ANCESTOR = 1 << 6;
19 -export const IS_TARGET_EVENT_ONLY = 1 << 7;
14 +export const USE_EVENT_SYSTEM = 1 << 2;
15 +export const IS_TARGET_PHASE_ONLY = 1 << 3;
16 +export const IS_PASSIVE = 1 << 4;
17 +export const PASSIVE_NOT_SUPPORTED = 1 << 5;
18 +export const IS_REPLAYED = 1 << 6;
19 +export const IS_FIRST_ANCESTOR = 1 << 7;
20 export const LEGACY_FB_SUPPORT = 1 << 8;
packages/react-dom/src/__tests__/ReactDOM-test.js
+25 -4
@@ -13,6 +13,7 @@ let React;
13 let ReactDOM;
14 let ReactDOMServer;
15 let ReactTestUtils;
16 +const ReactFeatureFlags = require('shared/ReactFeatureFlags');
17
18 describe('ReactDOM', () => {
19 beforeEach(() => {
@@ -354,11 +355,31 @@ describe('ReactDOM', () => {
355 document.body.appendChild(container);
356 try {
357 ReactDOM.render(<Wrapper />, container);
358 + let expected;
359 +
360 + if (
361 + ReactFeatureFlags.enableModernEventSystem &
362 + ReactFeatureFlags.enableLegacyFBSupport
363 + ) {
364 + // We expect to duplicate the 2nd handler because this test is
365 + // not really designed around how the legacy FB support system works.
366 + // This is because the above test sync fires a click() event
367 + // during that of another click event, which causes the FB support system
368 + // to duplicate adding an event listener. In practice this would never
369 + // happen, as we only apply the legacy FB logic for "click" events,
370 + // which would never stack this way in product code.
371 + expected = [
372 + '1st node clicked',
373 + "2nd node clicked imperatively from 1st's handler",
374 + "2nd node clicked imperatively from 1st's handler",
375 + ];
376 + } else {
377 + expected = [
378 + '1st node clicked',
379 + "2nd node clicked imperatively from 1st's handler",
380 + ];
381 + }
382
358 - const expected = [
359 - '1st node clicked',
360 - "2nd node clicked imperatively from 1st's handler",
361 - ];
383 expect(actual).toEqual(expected);
384 } finally {
385 document.body.removeChild(container);
packages/react-dom/src/__tests__/ReactDOMComponent-test.js
+25 -8
@@ -14,6 +14,7 @@ describe('ReactDOMComponent', () => {
14 let ReactTestUtils;
15 let ReactDOM;
16 let ReactDOMServer;
17 + const ReactFeatureFlags = require('shared/ReactFeatureFlags');
18
19 function normalizeCodeLocInfo(str) {
20 return str && str.replace(/\(at .+?:\d+\)/g, '(at **)');
@@ -2595,14 +2596,30 @@ describe('ReactDOMComponent', () => {
2596 // might depend on this.
2597 //
2598 // @see https://github.com/facebook/react/pull/12919#issuecomment-395224674
2598 - expect(eventOrder).toEqual([
2599 - 'document capture',
2600 - 'inner capture',
2601 - 'inner bubble',
2602 - 'outer capture',
2603 - 'outer bubble',
2604 - 'document bubble',
2605 - ]);
2599 + if (
2600 + ReactFeatureFlags.enableModernEventSystem &
2601 + ReactFeatureFlags.enableLegacyFBSupport
2602 + ) {
2603 + // The order will change here, as the legacy FB support adds
2604 + // the event listener onto the document after the one above has.
2605 + expect(eventOrder).toEqual([
2606 + 'document capture',
2607 + 'document bubble',
2608 + 'inner capture',
2609 + 'inner bubble',
2610 + 'outer capture',
2611 + 'outer bubble',
2612 + ]);
2613 + } else {
2614 + expect(eventOrder).toEqual([
2615 + 'document capture',
2616 + 'inner capture',
2617 + 'inner bubble',
2618 + 'outer capture',
2619 + 'outer bubble',
2620 + 'document bubble',
2621 + ]);
2622 + }
2623 } finally {
2624 document.body.removeChild(container);
2625 }
packages/react-dom/src/client/ReactDOMHostConfig.js
+3
@@ -76,6 +76,8 @@ import {HostComponent} from 'react-reconciler/src/ReactWorkTags';
76 import {
77 RESPONDER_EVENT_SYSTEM,
78 IS_PASSIVE,
79 + PLUGIN_EVENT_SYSTEM,
80 + USE_EVENT_SYSTEM,
81 } from 'legacy-events/EventSystemFlags';
82 import {
83 isManagedDOMElement,
@@ -1156,6 +1158,7 @@ export function registerEvent(
1158 type,
1159 rootContainerInstance,
1160 listenerMap,
1161 + PLUGIN_EVENT_SYSTEM | USE_EVENT_SYSTEM,
1162 passive,
1163 priority,
1164 );
packages/react-dom/src/events/DOMLegacyEventPluginSystem.js
+16 -3
@@ -21,7 +21,10 @@ import {
21 HostComponent,
22 HostText,
23 } from 'react-reconciler/src/ReactWorkTags';
24 -import {IS_FIRST_ANCESTOR} from 'legacy-events/EventSystemFlags';
24 +import {
25 + IS_FIRST_ANCESTOR,
26 + PLUGIN_EVENT_SYSTEM,
27 +} from 'legacy-events/EventSystemFlags';
28 import {batchedEventUpdates} from 'legacy-events/ReactGenericBatching';
29 import {runEventsInBatch} from 'legacy-events/EventBatching';
30 import {plugins} from 'legacy-events/EventPluginRegistry';
@@ -372,7 +375,12 @@ export function legacyTrapBubbledEvent(
375 element: Document | Element,
376 listenerMap?: ElementListenerMap,
377 ): void {
375 - const listener = addTrappedEventListener(element, topLevelType, false);
378 + const listener = addTrappedEventListener(
379 + element,
380 + topLevelType,
381 + PLUGIN_EVENT_SYSTEM,
382 + false,
383 + );
384 if (listenerMap) {
385 listenerMap.set(topLevelType, {passive: undefined, listener});
386 }
@@ -383,6 +391,11 @@ export function legacyTrapCapturedEvent(
391 element: Document | Element,
392 listenerMap: ElementListenerMap,
393 ): void {
386 - const listener = addTrappedEventListener(element, topLevelType, true);
394 + const listener = addTrappedEventListener(
395 + element,
396 + topLevelType,
397 + PLUGIN_EVENT_SYSTEM,
398 + true,
399 + );
400 listenerMap.set(topLevelType, {passive: undefined, listener});
401 }
packages/react-dom/src/events/DOMModernPluginEventSystem.js
+17 -4
@@ -28,9 +28,11 @@ import {batchedEventUpdates} from 'legacy-events/ReactGenericBatching';
28 import {executeDispatchesInOrder} from 'legacy-events/EventPluginUtils';
29 import {plugins} from 'legacy-events/EventPluginRegistry';
30 import {
31 + PLUGIN_EVENT_SYSTEM,
32 LEGACY_FB_SUPPORT,
33 IS_REPLAYED,
33 - IS_TARGET_EVENT_ONLY,
34 + IS_TARGET_PHASE_ONLY,
35 + USE_EVENT_SYSTEM,
36 } from 'legacy-events/EventSystemFlags';
37
38 import {HostRoot, HostPortal} from 'react-reconciler/src/ReactWorkTags';
@@ -229,6 +231,7 @@ export function listenToTopLevelEvent(
231 topLevelType: DOMTopLevelEventType,
232 targetContainer: EventTarget,
233 listenerMap: ElementListenerMap,
234 + eventSystemFlags: EventSystemFlags,
235 passive?: boolean,
236 priority?: EventPriority,
237 capture?: boolean,
@@ -265,6 +268,7 @@ export function listenToTopLevelEvent(
268 const listener = addTrappedEventListener(
269 targetContainer,
270 topLevelType,
271 + eventSystemFlags,
272 isCapturePhase,
273 false,
274 passive,
@@ -283,7 +287,12 @@ export function listenToEvent(
287
288 for (let i = 0; i < dependencies.length; i++) {
289 const dependency = dependencies[i];
286 - listenToTopLevelEvent(dependency, rootContainerElement, listenerMap);
290 + listenToTopLevelEvent(
291 + dependency,
292 + rootContainerElement,
293 + listenerMap,
294 + PLUGIN_EVENT_SYSTEM,
295 + );
296 }
297 }
298
@@ -301,6 +310,7 @@ function willDeferLaterForLegacyFBSupport(
310 addTrappedEventListener(
311 targetContainer,
312 topLevelType,
313 + PLUGIN_EVENT_SYSTEM | LEGACY_FB_SUPPORT,
314 false,
315 isDeferredListenerForLegacyFBSupport,
316 );
@@ -343,10 +353,9 @@ export function dispatchEventForPluginEventSystem(
353 ): void {
354 let ancestorInst = targetInst;
355 if (targetContainer !== null) {
346 - if (eventTargetEventListenerStore.has(targetContainer)) {
356 + if (eventSystemFlags & IS_TARGET_PHASE_ONLY) {
357 // For TargetEvent nodes (i.e. document, window)
358 ancestorInst = null;
349 - eventSystemFlags |= IS_TARGET_EVENT_ONLY;
359 } else {
360 const targetContainerNode = ((targetContainer: any): Node);
361
@@ -362,6 +371,8 @@ export function dispatchEventForPluginEventSystem(
371 (eventSystemFlags & LEGACY_FB_SUPPORT) === 0 &&
372 // We also don't want to defer during event replaying.
373 (eventSystemFlags & IS_REPLAYED) === 0 &&
374 + // We don't want to apply the legacy FB support for the useEvent API.
375 + (eventSystemFlags & USE_EVENT_SYSTEM) === 0 &&
376 willDeferLaterForLegacyFBSupport(topLevelType, targetContainer)
377 ) {
378 return;
@@ -483,6 +494,7 @@ export function attachListenerToManagedDOMElement(
494 type,
495 containerEventTarget,
496 listenerMap,
497 + PLUGIN_EVENT_SYSTEM | USE_EVENT_SYSTEM,
498 passive,
499 priority,
500 );
@@ -522,6 +534,7 @@ export function attachTargetEventListener(listener: ReactDOMListener): void {
534 type,
535 eventTarget,
536 listenerMap,
537 + PLUGIN_EVENT_SYSTEM | USE_EVENT_SYSTEM | IS_TARGET_PHASE_ONLY,
538 passive,
539 priority,
540 capture,
packages/react-dom/src/events/ReactDOMEventListener.js
+10 -10
@@ -37,12 +37,11 @@ import {
37 import {HostRoot, SuspenseComponent} from 'react-reconciler/src/ReactWorkTags';
38 import {
39 type EventSystemFlags,
40 + LEGACY_FB_SUPPORT,
41 PLUGIN_EVENT_SYSTEM,
42 RESPONDER_EVENT_SYSTEM,
43 IS_PASSIVE,
43 - IS_ACTIVE,
44 PASSIVE_NOT_SUPPORTED,
45 - LEGACY_FB_SUPPORT,
45 } from 'legacy-events/EventSystemFlags';
46
47 import {
@@ -103,12 +102,9 @@ export function addResponderEventSystemEvent(
102 if (passiveBrowserEventsSupported) {
103 eventFlags |= IS_PASSIVE;
104 } else {
106 - eventFlags |= IS_ACTIVE;
105 eventFlags |= PASSIVE_NOT_SUPPORTED;
106 passive = false;
107 }
110 - } else {
111 - eventFlags |= IS_ACTIVE;
108 }
109 // Check if interactive and wrap in discreteUpdates
110 const listener = dispatchEvent.bind(
@@ -132,6 +128,7 @@ export function addResponderEventSystemEvent(
128 export function addTrappedEventListener(
129 targetContainer: EventTarget,
130 topLevelType: DOMTopLevelEventType,
131 + eventSystemFlags: EventSystemFlags,
132 capture: boolean,
133 isDeferredListenerForLegacyFBSupport?: boolean,
134 passive?: boolean,
@@ -160,10 +157,6 @@ export function addTrappedEventListener(
157 if (passive === true && !passiveBrowserEventsSupported) {
158 passive = false;
159 }
163 - const eventSystemFlags =
164 - enableLegacyFBSupport && isDeferredListenerForLegacyFBSupport
165 - ? PLUGIN_EVENT_SYSTEM | LEGACY_FB_SUPPORT
166 - : PLUGIN_EVENT_SYSTEM;
160
161 listener = listenerWrapper.bind(
162 null,
@@ -268,7 +261,14 @@ function dispatchDiscreteEvent(
261 container,
262 nativeEvent,
263 ) {
271 - flushDiscreteUpdatesIfNeeded(nativeEvent.timeStamp);
264 + if (
265 + !enableLegacyFBSupport ||
266 + // If we have Legacy FB support, it means we've already
267 + // flushed for this event and we don't need to do it again.
268 + (eventSystemFlags & LEGACY_FB_SUPPORT) === 0
269 + ) {
270 + flushDiscreteUpdatesIfNeeded(nativeEvent.timeStamp);
271 + }
272 discreteUpdates(
273 dispatchEvent,
274 topLevelType,
packages/react-dom/src/events/ReactDOMEventReplaying.js
+7 -2
@@ -118,7 +118,7 @@ import {
118 TOP_FOCUS,
119 TOP_BLUR,
120 } from './DOMTopLevelEventTypes';
121 -import {IS_REPLAYED} from 'legacy-events/EventSystemFlags';
121 +import {IS_REPLAYED, PLUGIN_EVENT_SYSTEM} from 'legacy-events/EventSystemFlags';
122 import {legacyListenToTopLevelEvent} from './DOMLegacyEventPluginSystem';
123 import {listenToTopLevelEvent} from './DOMModernPluginEventSystem';
124
@@ -219,7 +219,12 @@ function trapReplayableEventForContainer(
219 container: Container,
220 listenerMap: ElementListenerMap,
221 ) {
222 - listenToTopLevelEvent(topLevelType, ((container: any): Element), listenerMap);
222 + listenToTopLevelEvent(
223 + topLevelType,
224 + ((container: any): Element),
225 + listenerMap,
226 + PLUGIN_EVENT_SYSTEM,
227 + );
228 }
229
230 function trapReplayableEventForDocument(
packages/react-dom/src/events/SimpleEventPlugin.js
+3 -2
@@ -17,6 +17,7 @@ import type {PluginModule} from 'legacy-events/PluginModuleType';
17 import type {EventSystemFlags} from 'legacy-events/EventSystemFlags';
18
19 import SyntheticEvent from 'legacy-events/SyntheticEvent';
20 +import {IS_TARGET_PHASE_ONLY} from 'legacy-events/EventSystemFlags';
21
22 import * as DOMTopLevelEventTypes from './DOMTopLevelEventTypes';
23 import {
@@ -38,7 +39,7 @@ import SyntheticWheelEvent from './SyntheticWheelEvent';
39 import getEventCharCode from './getEventCharCode';
40 import accumulateTwoPhaseListeners from './accumulateTwoPhaseListeners';
41 import accumulateEventTargetListeners from './accumulateEventTargetListeners';
41 -import {IS_TARGET_EVENT_ONLY} from 'legacy-events/EventSystemFlags';
42 +
43 import {enableUseEventAPI} from 'shared/ReactFeatureFlags';
44
45 // Only used in DEV for exhaustiveness validation.
@@ -210,7 +211,7 @@ const SimpleEventPlugin: PluginModule<MouseEvent> = {
211 if (
212 enableUseEventAPI &&
213 eventSystemFlags !== undefined &&
213 - eventSystemFlags & IS_TARGET_EVENT_ONLY &&
214 + eventSystemFlags & IS_TARGET_PHASE_ONLY &&
215 targetContainer != null
216 ) {
217 accumulateEventTargetListeners(event, targetContainer);