@samitouri / QOS-React-2 / commits / b34f042e5b

Fix mouseenter handlers fired twice (#16928)

Rango Yuan committed Oct 1, 2019 at 20:03 UTC b34f042e5b6fa30c645b71d38a0b1ecf785af2cc
4 files changed +130
fixtures/dom/src/components/fixtures/mouse-events/index.js
+2
@@ -1,5 +1,6 @@
1 import FixtureSet from '../../FixtureSet';
2 import MouseMovement from './mouse-movement';
3 +import MouseEnter from './mouse-enter';
4
5 const React = window.React;
6
@@ -8,6 +9,7 @@ class MouseEvents extends React.Component {
9 return (
10 <FixtureSet title="Mouse Events">
11 <MouseMovement />
12 + <MouseEnter />
13 </FixtureSet>
14 );
15 }
fixtures/dom/src/components/fixtures/mouse-events/mouse-enter.js new
+73
@@ -0,0 +1,73 @@
1 +import TestCase from '../../TestCase';
2 +
3 +const React = window.React;
4 +const ReactDOM = window.ReactDOM;
5 +
6 +const MouseEnter = () => {
7 + const containerRef = React.useRef();
8 +
9 + React.useEffect(function() {
10 + const hostEl = containerRef.current;
11 + ReactDOM.render(<MouseEnterDetect />, hostEl, () => {
12 + ReactDOM.render(<MouseEnterDetect />, hostEl.childNodes[1]);
13 + });
14 + }, []);
15 +
16 + return (
17 + <TestCase
18 + title="Mouse Enter"
19 + description=""
20 + affectedBrowsers="Chrome, Safari, Firefox">
21 + <TestCase.Steps>
22 + <li>Mouse enter the boxes below, from different borders</li>
23 + </TestCase.Steps>
24 + <TestCase.ExpectedResult>
25 + Mouse enter call count should equal to 1; <br />
26 + Issue{' '}
27 + <a
28 + rel="noopener noreferrer"
29 + target="_blank"
30 + href="https://github.com/facebook/react/issues/16763">
31 + #16763
32 + </a>{' '}
33 + should not happen.
34 + <br />
35 + </TestCase.ExpectedResult>
36 + <div ref={containerRef} />
37 + </TestCase>
38 + );
39 +};
40 +
41 +const MouseEnterDetect = () => {
42 + const [log, setLog] = React.useState({});
43 + const firstEl = React.useRef();
44 + const siblingEl = React.useRef();
45 +
46 + const onMouseEnter = e => {
47 + const timeStamp = e.timeStamp;
48 + setLog(log => {
49 + const callCount = 1 + (log.timeStamp === timeStamp ? log.callCount : 0);
50 + return {
51 + timeStamp,
52 + callCount,
53 + };
54 + });
55 + };
56 +
57 + return (
58 + <React.Fragment>
59 + <div
60 + ref={firstEl}
61 + onMouseEnter={onMouseEnter}
62 + style={{
63 + border: '1px solid #d9d9d9',
64 + padding: '20px 20px',
65 + }}>
66 + Mouse enter call count: {log.callCount || ''}
67 + </div>
68 + <div ref={siblingEl} />
69 + </React.Fragment>
70 + );
71 +};
72 +
73 +export default MouseEnter;
packages/react-dom/src/events/EnterLeaveEventPlugin.js
+4
@@ -163,6 +163,10 @@ const EnterLeaveEventPlugin = {
163
164 accumulateEnterLeaveDispatches(leave, enter, from, to);
165
166 + if (isOutEvent && from && nativeEventTarget !== fromNode) {
167 + return [leave];
168 + }
169 +
170 return [leave, enter];
171 },
172 };
packages/react-dom/src/events/__tests__/EnterLeaveEventPlugin-test.js
+51
@@ -134,4 +134,55 @@ describe('EnterLeaveEventPlugin', () => {
134 expect(childEnterCalls).toBe(1);
135 expect(parentEnterCalls).toBe(0);
136 });
137 +
138 + // Test for https://github.com/facebook/react/issues/16763.
139 + it('should call mouseEnter once from sibling rendered inside a rendered component', done => {
140 + const mockFn = jest.fn();
141 +
142 + class Parent extends React.Component {
143 + constructor(props) {
144 + super(props);
145 + this.parentEl = React.createRef();
146 + }
147 +
148 + componentDidMount() {
149 + ReactDOM.render(<MouseEnterDetect />, this.parentEl.current);
150 + }
151 +
152 + render() {
153 + return <div ref={this.parentEl} />;
154 + }
155 + }
156 +
157 + class MouseEnterDetect extends React.Component {
158 + constructor(props) {
159 + super(props);
160 + this.firstEl = React.createRef();
161 + this.siblingEl = React.createRef();
162 + }
163 +
164 + componentDidMount() {
165 + this.siblingEl.current.dispatchEvent(
166 + new MouseEvent('mouseout', {
167 + bubbles: true,
168 + cancelable: true,
169 + relatedTarget: this.firstEl.current,
170 + }),
171 + );
172 + expect(mockFn.mock.calls.length).toBe(1);
173 + done();
174 + }
175 +
176 + render() {
177 + return (
178 + <React.Fragment>
179 + <div ref={this.firstEl} onMouseEnter={mockFn} />
180 + <div ref={this.siblingEl} />
181 + </React.Fragment>
182 + );
183 + }
184 + }
185 +
186 + ReactDOM.render(<Parent />, container);
187 + });
188 });