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

Keep onTouchStart, onTouchMove, and onWheel passive (#19654)

* Keep onTouchStart, onTouchMove, and onWheel passive * Put it behind a feature flag on WWW

Dan Abramov committed Aug 19, 2020 at 18:42 UTC dd651df05e5802a31484f269b689c17acf087299
13 files changed +70 -4
packages/react-dom/src/events/DOMPluginEventSystem.js
+18 -2
@@ -53,6 +53,7 @@ import {
53 enableLegacyFBSupport,
54 enableCreateEventHandleAPI,
55 enableScopeAPI,
56 + enablePassiveEventIntervention,
57 } from 'shared/ReactFeatureFlags';
58 import {
59 invokeGuardedCallbackAndCatchFirstError,
@@ -342,6 +343,21 @@ export function listenToNativeEvent(
343 if (domEventName === 'selectionchange') {
344 target = (rootContainerElement: any).ownerDocument;
345 }
346 + if (enablePassiveEventIntervention && isPassiveListener === undefined) {
347 + // Browsers introduced an intervention, making these events
348 + // passive by default on document. React doesn't bind them
349 + // to document anymore, but changing this now would undo
350 + // the performance wins from the change. So we emulate
351 + // the existing behavior manually on the roots now.
352 + // https://github.com/facebook/react/issues/19651
353 + if (
354 + domEventName === 'touchstart' ||
355 + domEventName === 'touchmove' ||
356 + domEventName === 'wheel'
357 + ) {
358 + isPassiveListener = true;
359 + }
360 + }
361 // If the event can be delegated (or is capture phase), we can
362 // register it to the root container. Otherwise, we should
363 // register the event to the target element and mark it as
@@ -506,7 +522,7 @@ function addTrappedEventListener(
522 };
523 }
524 if (isCapturePhaseListener) {
509 - if (enableCreateEventHandleAPI && isPassiveListener !== undefined) {
525 + if (isPassiveListener !== undefined) {
526 unsubscribeListener = addEventCaptureListenerWithPassiveFlag(
527 targetContainer,
528 domEventName,
@@ -521,7 +537,7 @@ function addTrappedEventListener(
537 );
538 }
539 } else {
524 - if (enableCreateEventHandleAPI && isPassiveListener !== undefined) {
540 + if (isPassiveListener !== undefined) {
541 unsubscribeListener = addEventBubbleListenerWithPassiveFlag(
542 targetContainer,
543 domEventName,
packages/react-dom/src/events/checkPassiveEvents.js
+1 -2
@@ -8,13 +8,12 @@
8 */
9
10 import {canUseDOM} from 'shared/ExecutionEnvironment';
11 -import {enableCreateEventHandleAPI} from 'shared/ReactFeatureFlags';
11
12 export let passiveBrowserEventsSupported = false;
13
14 // Check if browser support events with passive listeners
15 // https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support
17 -if (enableCreateEventHandleAPI && canUseDOM) {
16 +if (canUseDOM) {
17 try {
18 const options = {};
19 // $FlowFixMe: Ignore Flow complaining about needing a value
packages/react-dom/src/events/plugins/__tests__/SimpleEventPlugin-test.js
+39
@@ -504,5 +504,44 @@ describe('SimpleEventPlugin', function() {
504
505 expect(onClick).toHaveBeenCalledTimes(0);
506 });
507 +
508 + it('registers passive handlers for events affected by the intervention', () => {
509 + container = document.createElement('div');
510 +
511 + const passiveEvents = [];
512 + const nativeAddEventListener = container.addEventListener;
513 + container.addEventListener = function(type, fn, options) {
514 + if (options !== null && typeof options === 'object') {
515 + if (options.passive) {
516 + passiveEvents.push(type);
517 + }
518 + }
519 + return nativeAddEventListener.apply(this, arguments);
520 + };
521 +
522 + ReactDOM.render(
523 + <div
524 + // Affected by the intervention:
525 + // https://github.com/facebook/react/issues/19651
526 + onTouchStart={() => {}}
527 + onTouchMove={() => {}}
528 + onWheel={() => {}}
529 + // A few events that should be unaffected:
530 + onClick={() => {}}
531 + onScroll={() => {}}
532 + onTouchEnd={() => {}}
533 + onChange={() => {}}
534 + onPointerDown={() => {}}
535 + onPointerMove={() => {}}
536 + />,
537 + container,
538 + );
539 +
540 + if (gate(flags => flags.enablePassiveEventIntervention)) {
541 + expect(passiveEvents).toEqual(['touchstart', 'touchmove', 'wheel']);
542 + } else {
543 + expect(passiveEvents).toEqual([]);
544 + }
545 + });
546 });
547 });
packages/shared/ReactFeatureFlags.js
+3
@@ -132,3 +132,6 @@ export const deferRenderPhaseUpdateToNextBatch = true;
132 export const decoupleUpdatePriorityFromScheduler = false;
133
134 export const enableDiscreteEventFlushingChange = false;
135 +
136 +// https://github.com/facebook/react/pull/19654
137 +export const enablePassiveEventIntervention = true;
packages/shared/forks/ReactFeatureFlags.native-fb.js
+1
@@ -49,6 +49,7 @@ export const enableNewReconciler = false;
49 export const deferRenderPhaseUpdateToNextBatch = true;
50 export const decoupleUpdatePriorityFromScheduler = false;
51 export const enableDiscreteEventFlushingChange = false;
52 +export const enablePassiveEventIntervention = true;
53
54 // Flow magic to verify the exports of this file match the original version.
55 // eslint-disable-next-line no-unused-vars
packages/shared/forks/ReactFeatureFlags.native-oss.js
+1
@@ -48,6 +48,7 @@ export const enableNewReconciler = false;
48 export const deferRenderPhaseUpdateToNextBatch = true;
49 export const decoupleUpdatePriorityFromScheduler = false;
50 export const enableDiscreteEventFlushingChange = false;
51 +export const enablePassiveEventIntervention = true;
52
53 // Flow magic to verify the exports of this file match the original version.
54 // eslint-disable-next-line no-unused-vars
packages/shared/forks/ReactFeatureFlags.test-renderer.js
+1
@@ -48,6 +48,7 @@ export const enableNewReconciler = false;
48 export const deferRenderPhaseUpdateToNextBatch = true;
49 export const decoupleUpdatePriorityFromScheduler = false;
50 export const enableDiscreteEventFlushingChange = false;
51 +export const enablePassiveEventIntervention = true;
52
53 // Flow magic to verify the exports of this file match the original version.
54 // eslint-disable-next-line no-unused-vars
packages/shared/forks/ReactFeatureFlags.test-renderer.native.js
+1
@@ -48,6 +48,7 @@ export const enableNewReconciler = false;
48 export const deferRenderPhaseUpdateToNextBatch = true;
49 export const decoupleUpdatePriorityFromScheduler = false;
50 export const enableDiscreteEventFlushingChange = false;
51 +export const enablePassiveEventIntervention = true;
52
53 // Flow magic to verify the exports of this file match the original version.
54 // eslint-disable-next-line no-unused-vars
packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
+1
@@ -48,6 +48,7 @@ export const enableNewReconciler = false;
48 export const deferRenderPhaseUpdateToNextBatch = true;
49 export const decoupleUpdatePriorityFromScheduler = false;
50 export const enableDiscreteEventFlushingChange = false;
51 +export const enablePassiveEventIntervention = true;
52
53 // Flow magic to verify the exports of this file match the original version.
54 // eslint-disable-next-line no-unused-vars
packages/shared/forks/ReactFeatureFlags.testing.js
+1
@@ -48,6 +48,7 @@ export const enableNewReconciler = false;
48 export const deferRenderPhaseUpdateToNextBatch = true;
49 export const decoupleUpdatePriorityFromScheduler = false;
50 export const enableDiscreteEventFlushingChange = false;
51 +export const enablePassiveEventIntervention = true;
52
53 // Flow magic to verify the exports of this file match the original version.
54 // eslint-disable-next-line no-unused-vars
packages/shared/forks/ReactFeatureFlags.testing.www.js
+1
@@ -48,6 +48,7 @@ export const enableNewReconciler = false;
48 export const deferRenderPhaseUpdateToNextBatch = true;
49 export const decoupleUpdatePriorityFromScheduler = false;
50 export const enableDiscreteEventFlushingChange = true;
51 +export const enablePassiveEventIntervention = true;
52
53 // Flow magic to verify the exports of this file match the original version.
54 // eslint-disable-next-line no-unused-vars
packages/shared/forks/ReactFeatureFlags.www-dynamic.js
+1
@@ -19,6 +19,7 @@ export const enableFilterEmptyStringAttributesDOM = __VARIANT__;
19 export const enableLegacyFBSupport = __VARIANT__;
20 export const decoupleUpdatePriorityFromScheduler = __VARIANT__;
21 export const skipUnmountedBoundaries = __VARIANT__;
22 +export const enablePassiveEventIntervention = __VARIANT__;
23
24 // Enable this flag to help with concurrent mode debugging.
25 // It logs information to the console about React scheduling, rendering, and commit phases.
packages/shared/forks/ReactFeatureFlags.www.js
+1
@@ -27,6 +27,7 @@ export const {
27 decoupleUpdatePriorityFromScheduler,
28 enableDebugTracing,
29 skipUnmountedBoundaries,
30 + enablePassiveEventIntervention,
31 } = dynamicFeatureFlags;
32
33 // On WWW, __EXPERIMENTAL__ is used for a new modern build.