mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Keep onTouchStart, onTouchMove, and onWheel passive (#19654)
* Keep onTouchStart, onTouchMove, and onWheel passive * Put it behind a feature flag on WWW
This commit is contained in:
+18
-2
@@ -53,6 +53,7 @@ import {
|
||||
enableLegacyFBSupport,
|
||||
enableCreateEventHandleAPI,
|
||||
enableScopeAPI,
|
||||
enablePassiveEventIntervention,
|
||||
} from 'shared/ReactFeatureFlags';
|
||||
import {
|
||||
invokeGuardedCallbackAndCatchFirstError,
|
||||
@@ -342,6 +343,21 @@ export function listenToNativeEvent(
|
||||
if (domEventName === 'selectionchange') {
|
||||
target = (rootContainerElement: any).ownerDocument;
|
||||
}
|
||||
if (enablePassiveEventIntervention && isPassiveListener === undefined) {
|
||||
// Browsers introduced an intervention, making these events
|
||||
// passive by default on document. React doesn't bind them
|
||||
// to document anymore, but changing this now would undo
|
||||
// the performance wins from the change. So we emulate
|
||||
// the existing behavior manually on the roots now.
|
||||
// https://github.com/facebook/react/issues/19651
|
||||
if (
|
||||
domEventName === 'touchstart' ||
|
||||
domEventName === 'touchmove' ||
|
||||
domEventName === 'wheel'
|
||||
) {
|
||||
isPassiveListener = true;
|
||||
}
|
||||
}
|
||||
// If the event can be delegated (or is capture phase), we can
|
||||
// register it to the root container. Otherwise, we should
|
||||
// register the event to the target element and mark it as
|
||||
@@ -506,7 +522,7 @@ function addTrappedEventListener(
|
||||
};
|
||||
}
|
||||
if (isCapturePhaseListener) {
|
||||
if (enableCreateEventHandleAPI && isPassiveListener !== undefined) {
|
||||
if (isPassiveListener !== undefined) {
|
||||
unsubscribeListener = addEventCaptureListenerWithPassiveFlag(
|
||||
targetContainer,
|
||||
domEventName,
|
||||
@@ -521,7 +537,7 @@ function addTrappedEventListener(
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (enableCreateEventHandleAPI && isPassiveListener !== undefined) {
|
||||
if (isPassiveListener !== undefined) {
|
||||
unsubscribeListener = addEventBubbleListenerWithPassiveFlag(
|
||||
targetContainer,
|
||||
domEventName,
|
||||
|
||||
+1
-2
@@ -8,13 +8,12 @@
|
||||
*/
|
||||
|
||||
import {canUseDOM} from 'shared/ExecutionEnvironment';
|
||||
import {enableCreateEventHandleAPI} 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 (enableCreateEventHandleAPI && canUseDOM) {
|
||||
if (canUseDOM) {
|
||||
try {
|
||||
const options = {};
|
||||
// $FlowFixMe: Ignore Flow complaining about needing a value
|
||||
|
||||
@@ -504,5 +504,44 @@ describe('SimpleEventPlugin', function() {
|
||||
|
||||
expect(onClick).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it('registers passive handlers for events affected by the intervention', () => {
|
||||
container = document.createElement('div');
|
||||
|
||||
const passiveEvents = [];
|
||||
const nativeAddEventListener = container.addEventListener;
|
||||
container.addEventListener = function(type, fn, options) {
|
||||
if (options !== null && typeof options === 'object') {
|
||||
if (options.passive) {
|
||||
passiveEvents.push(type);
|
||||
}
|
||||
}
|
||||
return nativeAddEventListener.apply(this, arguments);
|
||||
};
|
||||
|
||||
ReactDOM.render(
|
||||
<div
|
||||
// Affected by the intervention:
|
||||
// https://github.com/facebook/react/issues/19651
|
||||
onTouchStart={() => {}}
|
||||
onTouchMove={() => {}}
|
||||
onWheel={() => {}}
|
||||
// A few events that should be unaffected:
|
||||
onClick={() => {}}
|
||||
onScroll={() => {}}
|
||||
onTouchEnd={() => {}}
|
||||
onChange={() => {}}
|
||||
onPointerDown={() => {}}
|
||||
onPointerMove={() => {}}
|
||||
/>,
|
||||
container,
|
||||
);
|
||||
|
||||
if (gate(flags => flags.enablePassiveEventIntervention)) {
|
||||
expect(passiveEvents).toEqual(['touchstart', 'touchmove', 'wheel']);
|
||||
} else {
|
||||
expect(passiveEvents).toEqual([]);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -132,3 +132,6 @@ export const deferRenderPhaseUpdateToNextBatch = true;
|
||||
export const decoupleUpdatePriorityFromScheduler = false;
|
||||
|
||||
export const enableDiscreteEventFlushingChange = false;
|
||||
|
||||
// https://github.com/facebook/react/pull/19654
|
||||
export const enablePassiveEventIntervention = true;
|
||||
|
||||
@@ -49,6 +49,7 @@ export const enableNewReconciler = false;
|
||||
export const deferRenderPhaseUpdateToNextBatch = true;
|
||||
export const decoupleUpdatePriorityFromScheduler = false;
|
||||
export const enableDiscreteEventFlushingChange = false;
|
||||
export const enablePassiveEventIntervention = true;
|
||||
|
||||
// Flow magic to verify the exports of this file match the original version.
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
|
||||
@@ -48,6 +48,7 @@ export const enableNewReconciler = false;
|
||||
export const deferRenderPhaseUpdateToNextBatch = true;
|
||||
export const decoupleUpdatePriorityFromScheduler = false;
|
||||
export const enableDiscreteEventFlushingChange = false;
|
||||
export const enablePassiveEventIntervention = true;
|
||||
|
||||
// Flow magic to verify the exports of this file match the original version.
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
|
||||
@@ -48,6 +48,7 @@ export const enableNewReconciler = false;
|
||||
export const deferRenderPhaseUpdateToNextBatch = true;
|
||||
export const decoupleUpdatePriorityFromScheduler = false;
|
||||
export const enableDiscreteEventFlushingChange = false;
|
||||
export const enablePassiveEventIntervention = true;
|
||||
|
||||
// Flow magic to verify the exports of this file match the original version.
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
|
||||
@@ -48,6 +48,7 @@ export const enableNewReconciler = false;
|
||||
export const deferRenderPhaseUpdateToNextBatch = true;
|
||||
export const decoupleUpdatePriorityFromScheduler = false;
|
||||
export const enableDiscreteEventFlushingChange = false;
|
||||
export const enablePassiveEventIntervention = true;
|
||||
|
||||
// Flow magic to verify the exports of this file match the original version.
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
|
||||
@@ -48,6 +48,7 @@ export const enableNewReconciler = false;
|
||||
export const deferRenderPhaseUpdateToNextBatch = true;
|
||||
export const decoupleUpdatePriorityFromScheduler = false;
|
||||
export const enableDiscreteEventFlushingChange = false;
|
||||
export const enablePassiveEventIntervention = true;
|
||||
|
||||
// Flow magic to verify the exports of this file match the original version.
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
|
||||
@@ -48,6 +48,7 @@ export const enableNewReconciler = false;
|
||||
export const deferRenderPhaseUpdateToNextBatch = true;
|
||||
export const decoupleUpdatePriorityFromScheduler = false;
|
||||
export const enableDiscreteEventFlushingChange = false;
|
||||
export const enablePassiveEventIntervention = true;
|
||||
|
||||
// Flow magic to verify the exports of this file match the original version.
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
|
||||
@@ -48,6 +48,7 @@ export const enableNewReconciler = false;
|
||||
export const deferRenderPhaseUpdateToNextBatch = true;
|
||||
export const decoupleUpdatePriorityFromScheduler = false;
|
||||
export const enableDiscreteEventFlushingChange = true;
|
||||
export const enablePassiveEventIntervention = true;
|
||||
|
||||
// Flow magic to verify the exports of this file match the original version.
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
|
||||
@@ -19,6 +19,7 @@ export const enableFilterEmptyStringAttributesDOM = __VARIANT__;
|
||||
export const enableLegacyFBSupport = __VARIANT__;
|
||||
export const decoupleUpdatePriorityFromScheduler = __VARIANT__;
|
||||
export const skipUnmountedBoundaries = __VARIANT__;
|
||||
export const enablePassiveEventIntervention = __VARIANT__;
|
||||
|
||||
// Enable this flag to help with concurrent mode debugging.
|
||||
// It logs information to the console about React scheduling, rendering, and commit phases.
|
||||
|
||||
@@ -27,6 +27,7 @@ export const {
|
||||
decoupleUpdatePriorityFromScheduler,
|
||||
enableDebugTracing,
|
||||
skipUnmountedBoundaries,
|
||||
enablePassiveEventIntervention,
|
||||
} = dynamicFeatureFlags;
|
||||
|
||||
// On WWW, __EXPERIMENTAL__ is used for a new modern build.
|
||||
|
||||
Reference in New Issue
Block a user