Fix issue with capture phase non-bubbling events (#19452)

This commit is contained in:
Dominic Gannaway
2020-07-25 12:32:21 +01:00
committed by GitHub
parent ef22aecfc5
commit 242a50a652
3 changed files with 51 additions and 1 deletions
@@ -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);
}
});
});
+7
View File
@@ -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
View File
@@ -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;