diff --git a/src/renderers/dom/shared/eventPlugins/__tests__/EnterLeaveEventPlugin-test.js b/src/renderers/dom/shared/eventPlugins/__tests__/EnterLeaveEventPlugin-test.js
index 1540236b8ed..c9bb7863a93 100644
--- a/src/renderers/dom/shared/eventPlugins/__tests__/EnterLeaveEventPlugin-test.js
+++ b/src/renderers/dom/shared/eventPlugins/__tests__/EnterLeaveEventPlugin-test.js
@@ -13,6 +13,7 @@ var EnterLeaveEventPlugin;
var React;
var ReactDOM;
var ReactDOMComponentTree;
+var ReactTestUtils;
describe('EnterLeaveEventPlugin', () => {
beforeEach(() => {
@@ -20,6 +21,7 @@ describe('EnterLeaveEventPlugin', () => {
React = require('react');
ReactDOM = require('react-dom');
+ ReactTestUtils = require('react-dom/test-utils');
// TODO: can we express this test with only public API?
ReactDOMComponentTree = require('ReactDOMComponentTree');
EnterLeaveEventPlugin = require('EnterLeaveEventPlugin');
@@ -58,4 +60,38 @@ describe('EnterLeaveEventPlugin', () => {
expect(enter.target).toBe(div);
expect(enter.relatedTarget).toBe(iframe.contentWindow);
});
+
+ // Regression test for https://github.com/facebook/react/issues/10906.
+ it('should find the common parent after updates', () => {
+ let parentEnterCalls = 0;
+ let childEnterCalls = 0;
+ let parent = null;
+ class Parent extends React.Component {
+ render() {
+ return (
+
parentEnterCalls++}
+ ref={node => (parent = node)}>
+ {this.props.showChild &&
+
childEnterCalls++} />}
+
+ );
+ }
+ }
+
+ const div = document.createElement('div');
+ ReactDOM.render(
, div);
+ // The issue only reproduced on insertion during the first update.
+ ReactDOM.render(
, div);
+
+ // Enter from parent into the child.
+ ReactTestUtils.simulateNativeEventOnNode('topMouseOut', parent, {
+ target: parent,
+ relatedTarget: parent.firstChild,
+ });
+
+ // Entering a child should fire on the child, not on the parent.
+ expect(childEnterCalls).toBe(1);
+ expect(parentEnterCalls).toBe(0);
+ });
});
diff --git a/src/renderers/shared/shared/ReactTreeTraversal.js b/src/renderers/shared/shared/ReactTreeTraversal.js
index 8b58d7b10b7..d483881530b 100644
--- a/src/renderers/shared/shared/ReactTreeTraversal.js
+++ b/src/renderers/shared/shared/ReactTreeTraversal.js
@@ -110,18 +110,38 @@ function traverseTwoPhase(inst, fn, arg) {
* "entered" or "left" that element.
*/
function traverseEnterLeave(from, to, fn, argFrom, argTo) {
- var common = from && to ? getLowestCommonAncestor(from, to) : null;
- var pathFrom = [];
- while (from && from !== common) {
+ const common = from && to ? getLowestCommonAncestor(from, to) : null;
+ const pathFrom = [];
+ while (true) {
+ if (!from) {
+ break;
+ }
+ if (from === common) {
+ break;
+ }
+ const alternate = from.alternate;
+ if (alternate !== null && alternate === common) {
+ break;
+ }
pathFrom.push(from);
from = getParent(from);
}
- var pathTo = [];
- while (to && to !== common) {
+ const pathTo = [];
+ while (true) {
+ if (!to) {
+ break;
+ }
+ if (to === common) {
+ break;
+ }
+ const alternate = to.alternate;
+ if (alternate !== null && alternate === common) {
+ break;
+ }
pathTo.push(to);
to = getParent(to);
}
- var i;
+ let i;
for (i = 0; i < pathFrom.length; i++) {
fn(pathFrom[i], 'bubbled', argFrom);
}