mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Fix issue with capture phase non-bubbling events (#19452)
This commit is contained in:
@@ -590,4 +590,36 @@ describe('ReactDOMEventListener', () => {
|
||||
document.body.removeChild(container);
|
||||
}
|
||||
});
|
||||
|
||||
it('should handle non-bubbling capture events correctly', () => {
|
||||
const container = document.createElement('div');
|
||||
const innerRef = React.createRef();
|
||||
const outerRef = React.createRef();
|
||||
const onPlayCapture = jest.fn();
|
||||
document.body.appendChild(container);
|
||||
try {
|
||||
ReactDOM.render(
|
||||
<div ref={outerRef} onPlayCapture={onPlayCapture}>
|
||||
<div onPlayCapture={onPlayCapture}>
|
||||
<div ref={innerRef} onPlayCapture={onPlayCapture} />
|
||||
</div>
|
||||
</div>,
|
||||
container,
|
||||
);
|
||||
innerRef.current.dispatchEvent(
|
||||
new Event('play', {
|
||||
bubbles: false,
|
||||
}),
|
||||
);
|
||||
expect(onPlayCapture).toHaveBeenCalledTimes(3);
|
||||
outerRef.current.dispatchEvent(
|
||||
new Event('play', {
|
||||
bubbles: false,
|
||||
}),
|
||||
);
|
||||
expect(onPlayCapture).toHaveBeenCalledTimes(4);
|
||||
} finally {
|
||||
document.body.removeChild(container);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -712,6 +712,7 @@ export function accumulateSinglePhaseListeners(
|
||||
dispatchQueue: DispatchQueue,
|
||||
event: ReactSyntheticEvent,
|
||||
inCapturePhase: boolean,
|
||||
accumulateTargetOnly: boolean,
|
||||
): void {
|
||||
const bubbled = event._reactName;
|
||||
const captured = bubbled !== null ? bubbled + 'Capture' : null;
|
||||
@@ -809,6 +810,12 @@ export function accumulateSinglePhaseListeners(
|
||||
}
|
||||
}
|
||||
}
|
||||
// If we are only accumulating events for the target, then we don't
|
||||
// continue to propagate through the React fiber tree to find other
|
||||
// listeners.
|
||||
if (accumulateTargetOnly) {
|
||||
break;
|
||||
}
|
||||
instance = instance.return;
|
||||
}
|
||||
if (listeners.length !== 0) {
|
||||
|
||||
+12
-1
@@ -40,7 +40,7 @@ import {
|
||||
import {IS_EVENT_HANDLE_NON_MANAGED_NODE} from '../EventSystemFlags';
|
||||
|
||||
import getEventCharCode from '../getEventCharCode';
|
||||
import {IS_CAPTURE_PHASE} from '../EventSystemFlags';
|
||||
import {IS_CAPTURE_PHASE, IS_NON_DELEGATED} from '../EventSystemFlags';
|
||||
|
||||
import {enableCreateEventHandleAPI} from 'shared/ReactFeatureFlags';
|
||||
|
||||
@@ -165,12 +165,23 @@ function extractEvents(
|
||||
inCapturePhase,
|
||||
);
|
||||
} else {
|
||||
// When we encounter a non-delegated event in the capture phase,
|
||||
// we shouldn't emuluate capture bubbling. This is because we'll
|
||||
// add a native capture event listener to each element directly,
|
||||
// not the root, and native capture listeners always fire even
|
||||
// if the event doesn't bubble.
|
||||
const isNonDelegatedEvent = (eventSystemFlags & IS_NON_DELEGATED) !== 0;
|
||||
// TODO: We may also want to re-use the accumulateTargetOnly flag to
|
||||
// special case bubbling for onScroll/media events at a later point.
|
||||
const accumulateTargetOnly = inCapturePhase && isNonDelegatedEvent;
|
||||
|
||||
// We traverse only capture or bubble phase listeners
|
||||
accumulateSinglePhaseListeners(
|
||||
targetInst,
|
||||
dispatchQueue,
|
||||
event,
|
||||
inCapturePhase,
|
||||
accumulateTargetOnly,
|
||||
);
|
||||
}
|
||||
return event;
|
||||
|
||||
Reference in New Issue
Block a user