Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit e16703e

Browse files
authored
Modern Event System: revise ancestor logic (facebook#18886)
1 parent 2b9d7cf commit e16703e

File tree

2 files changed

+107
-13
lines changed

2 files changed

+107
-13
lines changed

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

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -417,13 +417,13 @@ export function dispatchEventForPluginEventSystem(
417417
// sub-tree for that root and make that our ancestor instance.
418418
let node = targetInst;
419419

420-
while (true) {
420+
mainLoop: while (true) {
421421
if (node === null) {
422422
return;
423423
}
424424
const nodeTag = node.tag;
425425
if (nodeTag === HostRoot || nodeTag === HostPortal) {
426-
const container = node.stateNode.containerInfo;
426+
let container = node.stateNode.containerInfo;
427427
if (isMatchingRootContainer(container, targetContainerNode)) {
428428
break;
429429
}
@@ -449,18 +449,23 @@ export function dispatchEventForPluginEventSystem(
449449
grandNode = grandNode.return;
450450
}
451451
}
452-
const parentSubtreeInst = getClosestInstanceFromNode(container);
453-
if (parentSubtreeInst === null) {
454-
return;
455-
}
456-
const parentTag = parentSubtreeInst.tag;
457-
// getClosestInstanceFromNode can return a HostRoot or SuspenseComponent.
458-
// So we need to ensure we only set the ancestor to a HostComponent or HostText.
459-
if (parentTag === HostComponent || parentTag === HostText) {
460-
ancestorInst = parentSubtreeInst;
452+
// Now we need to find it's corresponding host fiber in the other
453+
// tree. To do this we can use getClosestInstanceFromNode, but we
454+
// need to validate that the fiber is a host instance, otherwise
455+
// we need to traverse up through the DOM till we find the correct
456+
// node that is from the other tree.
457+
while (container !== null) {
458+
const parentNode = getClosestInstanceFromNode(container);
459+
if (parentNode === null) {
460+
return;
461+
}
462+
const parentTag = parentNode.tag;
463+
if (parentTag === HostComponent || parentTag === HostText) {
464+
node = ancestorInst = parentNode;
465+
continue mainLoop;
466+
}
467+
container = container.parentNode;
461468
}
462-
node = parentSubtreeInst;
463-
continue;
464469
}
465470
node = node.return;
466471
}

packages/react-dom/src/events/__tests__/DOMModernPluginEventSystem-test.internal.js

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,95 @@ describe('DOMModernPluginEventSystem', () => {
224224
expect(log[5]).toEqual(['bubble', buttonElement]);
225225
});
226226

227+
it('handle propagation of click events between disjointed roots #2', () => {
228+
const buttonRef = React.createRef();
229+
const button2Ref = React.createRef();
230+
const divRef = React.createRef();
231+
const spanRef = React.createRef();
232+
const log = [];
233+
const onClick = jest.fn(e => log.push(['bubble', e.currentTarget]));
234+
const onClickCapture = jest.fn(e =>
235+
log.push(['capture', e.currentTarget]),
236+
);
237+
238+
function Child() {
239+
return (
240+
<div
241+
ref={divRef}
242+
onClick={onClick}
243+
onClickCapture={onClickCapture}>
244+
Click me!
245+
</div>
246+
);
247+
}
248+
249+
function Parent() {
250+
return (
251+
<button
252+
ref={button2Ref}
253+
onClick={onClick}
254+
onClickCapture={onClickCapture}
255+
/>
256+
);
257+
}
258+
259+
function GrandParent() {
260+
return (
261+
<button
262+
ref={buttonRef}
263+
onClick={onClick}
264+
onClickCapture={onClickCapture}>
265+
<span ref={spanRef} />
266+
</button>
267+
);
268+
}
269+
270+
// We make a wrapper with an inner container that we
271+
// render to. So it looks like <div><span></span></div>
272+
// We then render to all three:
273+
// - container
274+
// - parentContainer
275+
// - childContainer
276+
277+
const parentContainer = document.createElement('div');
278+
const childContainer = document.createElement('div');
279+
280+
ReactDOM.render(<GrandParent />, container);
281+
ReactDOM.render(<Parent />, parentContainer);
282+
ReactDOM.render(<Child />, childContainer);
283+
284+
parentContainer.appendChild(childContainer);
285+
spanRef.current.appendChild(parentContainer);
286+
287+
// Inside <GrandParent />
288+
const buttonElement = buttonRef.current;
289+
dispatchClickEvent(buttonElement);
290+
expect(onClick).toHaveBeenCalledTimes(1);
291+
expect(onClickCapture).toHaveBeenCalledTimes(1);
292+
expect(log[0]).toEqual(['capture', buttonElement]);
293+
expect(log[1]).toEqual(['bubble', buttonElement]);
294+
295+
// Inside <Child />
296+
const divElement = divRef.current;
297+
dispatchClickEvent(divElement);
298+
expect(onClick).toHaveBeenCalledTimes(3);
299+
expect(onClickCapture).toHaveBeenCalledTimes(3);
300+
expect(log[2]).toEqual(['capture', divElement]);
301+
expect(log[3]).toEqual(['bubble', divElement]);
302+
expect(log[4]).toEqual(['capture', buttonElement]);
303+
expect(log[5]).toEqual(['bubble', buttonElement]);
304+
305+
// Inside <Parent />
306+
const buttonElement2 = button2Ref.current;
307+
dispatchClickEvent(buttonElement2);
308+
expect(onClick).toHaveBeenCalledTimes(5);
309+
expect(onClickCapture).toHaveBeenCalledTimes(5);
310+
expect(log[6]).toEqual(['capture', buttonElement2]);
311+
expect(log[7]).toEqual(['bubble', buttonElement2]);
312+
expect(log[8]).toEqual(['capture', buttonElement]);
313+
expect(log[9]).toEqual(['bubble', buttonElement]);
314+
});
315+
227316
it('handle propagation of click events between disjointed comment roots', () => {
228317
const buttonRef = React.createRef();
229318
const divRef = React.createRef();

0 commit comments

Comments
 (0)