Skip to content

Commit dd651df

Browse files
authored
Keep onTouchStart, onTouchMove, and onWheel passive (facebook#19654)
* Keep onTouchStart, onTouchMove, and onWheel passive * Put it behind a feature flag on WWW
1 parent 87b3e2d commit dd651df

13 files changed

+70
-4
lines changed

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

+18-2
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ import {
5353
enableLegacyFBSupport,
5454
enableCreateEventHandleAPI,
5555
enableScopeAPI,
56+
enablePassiveEventIntervention,
5657
} from 'shared/ReactFeatureFlags';
5758
import {
5859
invokeGuardedCallbackAndCatchFirstError,
@@ -342,6 +343,21 @@ export function listenToNativeEvent(
342343
if (domEventName === 'selectionchange') {
343344
target = (rootContainerElement: any).ownerDocument;
344345
}
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+
}
345361
// If the event can be delegated (or is capture phase), we can
346362
// register it to the root container. Otherwise, we should
347363
// register the event to the target element and mark it as
@@ -506,7 +522,7 @@ function addTrappedEventListener(
506522
};
507523
}
508524
if (isCapturePhaseListener) {
509-
if (enableCreateEventHandleAPI && isPassiveListener !== undefined) {
525+
if (isPassiveListener !== undefined) {
510526
unsubscribeListener = addEventCaptureListenerWithPassiveFlag(
511527
targetContainer,
512528
domEventName,
@@ -521,7 +537,7 @@ function addTrappedEventListener(
521537
);
522538
}
523539
} else {
524-
if (enableCreateEventHandleAPI && isPassiveListener !== undefined) {
540+
if (isPassiveListener !== undefined) {
525541
unsubscribeListener = addEventBubbleListenerWithPassiveFlag(
526542
targetContainer,
527543
domEventName,

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

+1-2
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,12 @@
88
*/
99

1010
import {canUseDOM} from 'shared/ExecutionEnvironment';
11-
import {enableCreateEventHandleAPI} from 'shared/ReactFeatureFlags';
1211

1312
export let passiveBrowserEventsSupported = false;
1413

1514
// Check if browser support events with passive listeners
1615
// https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support
17-
if (enableCreateEventHandleAPI && canUseDOM) {
16+
if (canUseDOM) {
1817
try {
1918
const options = {};
2019
// $FlowFixMe: Ignore Flow complaining about needing a value

packages/react-dom/src/events/plugins/__tests__/SimpleEventPlugin-test.js

+39
Original file line numberDiff line numberDiff line change
@@ -504,5 +504,44 @@ describe('SimpleEventPlugin', function() {
504504

505505
expect(onClick).toHaveBeenCalledTimes(0);
506506
});
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+
});
507546
});
508547
});

packages/shared/ReactFeatureFlags.js

+3
Original file line numberDiff line numberDiff line change
@@ -132,3 +132,6 @@ export const deferRenderPhaseUpdateToNextBatch = true;
132132
export const decoupleUpdatePriorityFromScheduler = false;
133133

134134
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
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ export const enableNewReconciler = false;
4949
export const deferRenderPhaseUpdateToNextBatch = true;
5050
export const decoupleUpdatePriorityFromScheduler = false;
5151
export const enableDiscreteEventFlushingChange = false;
52+
export const enablePassiveEventIntervention = true;
5253

5354
// Flow magic to verify the exports of this file match the original version.
5455
// eslint-disable-next-line no-unused-vars

packages/shared/forks/ReactFeatureFlags.native-oss.js

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ export const enableNewReconciler = false;
4848
export const deferRenderPhaseUpdateToNextBatch = true;
4949
export const decoupleUpdatePriorityFromScheduler = false;
5050
export const enableDiscreteEventFlushingChange = false;
51+
export const enablePassiveEventIntervention = true;
5152

5253
// Flow magic to verify the exports of this file match the original version.
5354
// eslint-disable-next-line no-unused-vars

packages/shared/forks/ReactFeatureFlags.test-renderer.js

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ export const enableNewReconciler = false;
4848
export const deferRenderPhaseUpdateToNextBatch = true;
4949
export const decoupleUpdatePriorityFromScheduler = false;
5050
export const enableDiscreteEventFlushingChange = false;
51+
export const enablePassiveEventIntervention = true;
5152

5253
// Flow magic to verify the exports of this file match the original version.
5354
// eslint-disable-next-line no-unused-vars

packages/shared/forks/ReactFeatureFlags.test-renderer.native.js

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ export const enableNewReconciler = false;
4848
export const deferRenderPhaseUpdateToNextBatch = true;
4949
export const decoupleUpdatePriorityFromScheduler = false;
5050
export const enableDiscreteEventFlushingChange = false;
51+
export const enablePassiveEventIntervention = true;
5152

5253
// Flow magic to verify the exports of this file match the original version.
5354
// eslint-disable-next-line no-unused-vars

packages/shared/forks/ReactFeatureFlags.test-renderer.www.js

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ export const enableNewReconciler = false;
4848
export const deferRenderPhaseUpdateToNextBatch = true;
4949
export const decoupleUpdatePriorityFromScheduler = false;
5050
export const enableDiscreteEventFlushingChange = false;
51+
export const enablePassiveEventIntervention = true;
5152

5253
// Flow magic to verify the exports of this file match the original version.
5354
// eslint-disable-next-line no-unused-vars

packages/shared/forks/ReactFeatureFlags.testing.js

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ export const enableNewReconciler = false;
4848
export const deferRenderPhaseUpdateToNextBatch = true;
4949
export const decoupleUpdatePriorityFromScheduler = false;
5050
export const enableDiscreteEventFlushingChange = false;
51+
export const enablePassiveEventIntervention = true;
5152

5253
// Flow magic to verify the exports of this file match the original version.
5354
// eslint-disable-next-line no-unused-vars

packages/shared/forks/ReactFeatureFlags.testing.www.js

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ export const enableNewReconciler = false;
4848
export const deferRenderPhaseUpdateToNextBatch = true;
4949
export const decoupleUpdatePriorityFromScheduler = false;
5050
export const enableDiscreteEventFlushingChange = true;
51+
export const enablePassiveEventIntervention = true;
5152

5253
// Flow magic to verify the exports of this file match the original version.
5354
// eslint-disable-next-line no-unused-vars

packages/shared/forks/ReactFeatureFlags.www-dynamic.js

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export const enableFilterEmptyStringAttributesDOM = __VARIANT__;
1919
export const enableLegacyFBSupport = __VARIANT__;
2020
export const decoupleUpdatePriorityFromScheduler = __VARIANT__;
2121
export const skipUnmountedBoundaries = __VARIANT__;
22+
export const enablePassiveEventIntervention = __VARIANT__;
2223

2324
// Enable this flag to help with concurrent mode debugging.
2425
// It logs information to the console about React scheduling, rendering, and commit phases.

packages/shared/forks/ReactFeatureFlags.www.js

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export const {
2727
decoupleUpdatePriorityFromScheduler,
2828
enableDebugTracing,
2929
skipUnmountedBoundaries,
30+
enablePassiveEventIntervention,
3031
} = dynamicFeatureFlags;
3132

3233
// On WWW, __EXPERIMENTAL__ is used for a new modern build.

0 commit comments

Comments
 (0)