Fix createEventHandle bug with comment containers (#19348)
Dominic Gannaway committed
Jul 14, 2020 at 13:09 UTC
147179ae82039f38fa1a9a72402f578af8fb3ea3
2 files changed
+81
-2
packages/react-dom/src/client/ReactDOMEventHandle.js
+5
-2
@@ -22,7 +22,7 @@ import {
22
getEventListenerMap,
23
getFiberFromScopeInstance,
24
} from './ReactDOMComponentTree';
25
-import {ELEMENT_NODE} from '../shared/HTMLNodeType';
25
+import {ELEMENT_NODE, COMMENT_NODE} from '../shared/HTMLNodeType';
26
import {
27
listenToNativeEvent,
28
addEventTypeToDispatchConfig,
@@ -90,7 +90,7 @@ function registerEventOnNearestTargetContainer(
90
): void {
91
// If it is, find the nearest root or portal and make it
92
// our event handle target container.
93
- const targetContainer = getNearestRootOrPortalContainer(targetFiber);
93
+ let targetContainer = getNearestRootOrPortalContainer(targetFiber);
94
if (targetContainer === null) {
95
invariant(
96
false,
@@ -98,6 +98,9 @@ function registerEventOnNearestTargetContainer(
98
'that did not have a corresponding root. This is likely a bug in React.',
99
);
100
}
101
+ if (targetContainer.nodeType === COMMENT_NODE) {
102
+ targetContainer = ((targetContainer.parentNode: any): Element);
103
+ }
104
const listenerMap = getEventListenerMap(targetContainer);
105
listenToNativeEvent(
106
topLevelType,
packages/react-dom/src/events/__tests__/DOMModernPluginEventSystem-test.internal.js
+76
@@ -3009,6 +3009,82 @@ describe('DOMModernPluginEventSystem', () => {
3009
3010
expect(onClick).toHaveBeenCalledTimes(1);
3011
});
3012
+
3013
+ // @gate experimental
3014
+ it('handle propagation of click events between disjointed comment roots', () => {
3015
+ const buttonRef = React.createRef();
3016
+ const divRef = React.createRef();
3017
+ const log = [];
3018
+ const setClick = ReactDOM.unstable_createEventHandle('click');
3019
+ const setClickCapture = ReactDOM.unstable_createEventHandle(
3020
+ 'click',
3021
+ {capture: true},
3022
+ );
3023
+ const onClick = jest.fn(e =>
3024
+ log.push(['bubble', e.currentTarget]),
3025
+ );
3026
+ const onClickCapture = jest.fn(e =>
3027
+ log.push(['capture', e.currentTarget]),
3028
+ );
3029
+
3030
+ function Child() {
3031
+ React.useEffect(() => {
3032
+ const click1 = setClick(divRef.current, onClick);
3033
+ const click2 = setClickCapture(
3034
+ divRef.current,
3035
+ onClickCapture,
3036
+ );
3037
+ return () => {
3038
+ click1();
3039
+ click2();
3040
+ };
3041
+ });
3042
+
3043
+ return <div ref={divRef}>Click me!</div>;
3044
+ }
3045
+
3046
+ function Parent() {
3047
+ React.useEffect(() => {
3048
+ const click1 = setClick(buttonRef.current, onClick);
3049
+ const click2 = setClickCapture(
3050
+ buttonRef.current,
3051
+ onClickCapture,
3052
+ );
3053
+ return () => {
3054
+ click1();
3055
+ click2();
3056
+ };
3057
+ });
3058
+
3059
+ return <button ref={buttonRef} />;
3060
+ }
3061
+
3062
+ // We use a comment node here, then mount to it
3063
+ const disjointedNode = document.createComment(
3064
+ ' react-mount-point-unstable ',
3065
+ );
3066
+ ReactDOM.render(<Parent />, container);
3067
+ Scheduler.unstable_flushAll();
3068
+ buttonRef.current.appendChild(disjointedNode);
3069
+ ReactDOM.render(<Child />, disjointedNode);
3070
+ Scheduler.unstable_flushAll();
3071
+
3072
+ const buttonElement = buttonRef.current;
3073
+ dispatchClickEvent(buttonElement);
3074
+ expect(onClick).toHaveBeenCalledTimes(1);
3075
+ expect(onClickCapture).toHaveBeenCalledTimes(1);
3076
+ expect(log[0]).toEqual(['capture', buttonElement]);
3077
+ expect(log[1]).toEqual(['bubble', buttonElement]);
3078
+
3079
+ const divElement = divRef.current;
3080
+ dispatchClickEvent(divElement);
3081
+ expect(onClick).toHaveBeenCalledTimes(3);
3082
+ expect(onClickCapture).toHaveBeenCalledTimes(3);
3083
+ expect(log[2]).toEqual(['capture', buttonElement]);
3084
+ expect(log[3]).toEqual(['capture', divElement]);
3085
+ expect(log[4]).toEqual(['bubble', divElement]);
3086
+ expect(log[5]).toEqual(['bubble', buttonElement]);
3087
+ });
3088
});
3089
});
3090
},