[EnterLeaveEventPlugin] Fix bug when dealing with unhandled DOM nodes (#17006)
Dominic Gannaway committed
Oct 3, 2019 at 23:35 UTC
26ba38ae4b65375b97b561ec2d6a58ef597647ed
2 files changed
+60
-1
packages/react-dom/src/events/EnterLeaveEventPlugin.js
+9
-1
@@ -42,6 +42,12 @@ const eventTypes = {
42
},
43
};
44
45
+// We track the lastNativeEvent to ensure that when we encounter
46
+// cases where we process the same nativeEvent multiple times,
47
+// which can happen when have multiple ancestors, that we don't
48
+// duplicate enter
49
+let lastNativeEvent;
50
+
51
const EnterLeaveEventPlugin = {
52
eventTypes: eventTypes,
53
@@ -163,9 +169,11 @@ const EnterLeaveEventPlugin = {
169
170
accumulateEnterLeaveDispatches(leave, enter, from, to);
171
166
- if (isOutEvent && from && nativeEventTarget !== fromNode) {
172
+ if (nativeEvent === lastNativeEvent) {
173
+ lastNativeEvent = null;
174
return [leave];
175
}
176
+ lastNativeEvent = nativeEvent;
177
178
return [leave, enter];
179
},
packages/react-dom/src/events/__tests__/EnterLeaveEventPlugin-test.js
+51
@@ -185,4 +185,55 @@ describe('EnterLeaveEventPlugin', () => {
185
186
ReactDOM.render(<Parent />, container);
187
});
188
+
189
+ it('should call mouseEnter when pressing a non tracked React node', done => {
190
+ const mockFn = jest.fn();
191
+
192
+ class Parent extends React.Component {
193
+ constructor(props) {
194
+ super(props);
195
+ this.parentEl = React.createRef();
196
+ }
197
+
198
+ componentDidMount() {
199
+ ReactDOM.render(<MouseEnterDetect />, this.parentEl.current);
200
+ }
201
+
202
+ render() {
203
+ return <div ref={this.parentEl} />;
204
+ }
205
+ }
206
+
207
+ class MouseEnterDetect extends React.Component {
208
+ constructor(props) {
209
+ super(props);
210
+ this.divRef = React.createRef();
211
+ this.siblingEl = React.createRef();
212
+ }
213
+
214
+ componentDidMount() {
215
+ const attachedNode = document.createElement('div');
216
+ this.divRef.current.appendChild(attachedNode);
217
+ attachedNode.dispatchEvent(
218
+ new MouseEvent('mouseout', {
219
+ bubbles: true,
220
+ cancelable: true,
221
+ relatedTarget: this.siblingEl.current,
222
+ }),
223
+ );
224
+ expect(mockFn.mock.calls.length).toBe(1);
225
+ done();
226
+ }
227
+
228
+ render() {
229
+ return (
230
+ <div ref={this.divRef}>
231
+ <div ref={this.siblingEl} onMouseEnter={mockFn} />
232
+ </div>
233
+ );
234
+ }
235
+ }
236
+
237
+ ReactDOM.render(<Parent />, container);
238
+ });
239
});