Skip to content

Commit

Permalink
Fix mouseenter handlers fired twice (#16928)
Browse files Browse the repository at this point in the history
  • Loading branch information
yuanoook authored and trueadm committed Oct 1, 2019
1 parent 05dc814 commit b34f042
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 0 deletions.
2 changes: 2 additions & 0 deletions fixtures/dom/src/components/fixtures/mouse-events/index.js
@@ -1,5 +1,6 @@
import FixtureSet from '../../FixtureSet';
import MouseMovement from './mouse-movement';
import MouseEnter from './mouse-enter';

const React = window.React;

Expand All @@ -8,6 +9,7 @@ class MouseEvents extends React.Component {
return (
<FixtureSet title="Mouse Events">
<MouseMovement />
<MouseEnter />
</FixtureSet>
);
}
Expand Down
73 changes: 73 additions & 0 deletions fixtures/dom/src/components/fixtures/mouse-events/mouse-enter.js
@@ -0,0 +1,73 @@
import TestCase from '../../TestCase';

const React = window.React;
const ReactDOM = window.ReactDOM;

const MouseEnter = () => {
const containerRef = React.useRef();

React.useEffect(function() {
const hostEl = containerRef.current;
ReactDOM.render(<MouseEnterDetect />, hostEl, () => {
ReactDOM.render(<MouseEnterDetect />, hostEl.childNodes[1]);
});
}, []);

return (
<TestCase
title="Mouse Enter"
description=""
affectedBrowsers="Chrome, Safari, Firefox">
<TestCase.Steps>
<li>Mouse enter the boxes below, from different borders</li>
</TestCase.Steps>
<TestCase.ExpectedResult>
Mouse enter call count should equal to 1; <br />
Issue{' '}
<a
rel="noopener noreferrer"
target="_blank"
href="https://github.com/facebook/react/issues/16763">
#16763
</a>{' '}
should not happen.
<br />
</TestCase.ExpectedResult>
<div ref={containerRef} />
</TestCase>
);
};

const MouseEnterDetect = () => {
const [log, setLog] = React.useState({});
const firstEl = React.useRef();
const siblingEl = React.useRef();

const onMouseEnter = e => {
const timeStamp = e.timeStamp;
setLog(log => {
const callCount = 1 + (log.timeStamp === timeStamp ? log.callCount : 0);
return {
timeStamp,
callCount,
};
});
};

return (
<React.Fragment>
<div
ref={firstEl}
onMouseEnter={onMouseEnter}
style={{
border: '1px solid #d9d9d9',
padding: '20px 20px',
}}>
Mouse enter call count: {log.callCount || ''}
</div>
<div ref={siblingEl} />
</React.Fragment>
);
};

export default MouseEnter;
4 changes: 4 additions & 0 deletions packages/react-dom/src/events/EnterLeaveEventPlugin.js
Expand Up @@ -163,6 +163,10 @@ const EnterLeaveEventPlugin = {

accumulateEnterLeaveDispatches(leave, enter, from, to);

if (isOutEvent && from && nativeEventTarget !== fromNode) {
return [leave];
}

return [leave, enter];
},
};
Expand Down
Expand Up @@ -134,4 +134,55 @@ describe('EnterLeaveEventPlugin', () => {
expect(childEnterCalls).toBe(1);
expect(parentEnterCalls).toBe(0);
});

// Test for https://github.com/facebook/react/issues/16763.
it('should call mouseEnter once from sibling rendered inside a rendered component', done => {
const mockFn = jest.fn();

class Parent extends React.Component {
constructor(props) {
super(props);
this.parentEl = React.createRef();
}

componentDidMount() {
ReactDOM.render(<MouseEnterDetect />, this.parentEl.current);
}

render() {
return <div ref={this.parentEl} />;
}
}

class MouseEnterDetect extends React.Component {
constructor(props) {
super(props);
this.firstEl = React.createRef();
this.siblingEl = React.createRef();
}

componentDidMount() {
this.siblingEl.current.dispatchEvent(
new MouseEvent('mouseout', {
bubbles: true,
cancelable: true,
relatedTarget: this.firstEl.current,
}),
);
expect(mockFn.mock.calls.length).toBe(1);
done();
}

render() {
return (
<React.Fragment>
<div ref={this.firstEl} onMouseEnter={mockFn} />
<div ref={this.siblingEl} />
</React.Fragment>
);
}
}

ReactDOM.render(<Parent />, container);
});
});

0 comments on commit b34f042

Please sign in to comment.