Add more non-React events to the priority list (#20774)
* Add a test for mouseover being continuous * Add more non-React events to the priority list
Dan Abramov committed
Feb 9, 2021 at 22:04 UTC
ad8211d96a7990a91d4f6c9915b8b30566c3c0af
3 files changed
+90
packages/react-dom/src/__tests__/ReactDOMNativeEventHeuristic-test.js
+66
@@ -13,6 +13,7 @@ let React;
13
14
let ReactDOM;
15
let Scheduler;
16
+let act;
17
18
describe('ReactDOMNativeEventHeuristic-test', () => {
19
let container;
@@ -23,6 +24,7 @@ describe('ReactDOMNativeEventHeuristic-test', () => {
24
React = require('react');
25
ReactDOM = require('react-dom');
26
Scheduler = require('scheduler');
27
+ act = require('react-dom/test-utils').unstable_concurrentAct;
28
29
document.body.appendChild(container);
30
});
@@ -225,4 +227,68 @@ describe('ReactDOMNativeEventHeuristic-test', () => {
227
// Therefore the form should have been submitted.
228
expect(formSubmitted).toBe(true);
229
});
230
+
231
+ // @gate experimental
232
+ // @gate enableDiscreteEventMicroTasks && enableNativeEventPriorityInference
233
+ it('mouse over should be user-blocking but not discrete', async () => {
234
+ const root = ReactDOM.unstable_createRoot(container);
235
+
236
+ const target = React.createRef(null);
237
+ function Foo() {
238
+ const [isHover, setHover] = React.useState(false);
239
+ React.useLayoutEffect(() => {
240
+ target.current.onmouseover = () => setHover(true);
241
+ });
242
+ return <div ref={target}>{isHover ? 'hovered' : 'not hovered'}</div>;
243
+ }
244
+
245
+ await act(async () => {
246
+ root.render(<Foo />);
247
+ });
248
+ expect(container.textContent).toEqual('not hovered');
249
+
250
+ await act(async () => {
251
+ const mouseOverEvent = document.createEvent('MouseEvents');
252
+ mouseOverEvent.initEvent('mouseover', true, true);
253
+ dispatchAndSetCurrentEvent(target.current, mouseOverEvent);
254
+
255
+ // 3s should be enough to expire the updates
256
+ Scheduler.unstable_advanceTime(3000);
257
+ expect(Scheduler).toFlushExpired([]);
258
+ expect(container.textContent).toEqual('hovered');
259
+ });
260
+ });
261
+
262
+ // @gate experimental
263
+ // @gate enableDiscreteEventMicroTasks && enableNativeEventPriorityInference
264
+ it('mouse enter should be user-blocking but not discrete', async () => {
265
+ const root = ReactDOM.unstable_createRoot(container);
266
+
267
+ const target = React.createRef(null);
268
+ function Foo() {
269
+ const [isHover, setHover] = React.useState(false);
270
+ React.useLayoutEffect(() => {
271
+ target.current.onmouseenter = () => setHover(true);
272
+ });
273
+ return <div ref={target}>{isHover ? 'hovered' : 'not hovered'}</div>;
274
+ }
275
+
276
+ await act(async () => {
277
+ root.render(<Foo />);
278
+ });
279
+ expect(container.textContent).toEqual('not hovered');
280
+
281
+ await act(async () => {
282
+ // Note: React does not use native mouseenter/mouseleave events
283
+ // but we should still correctly determine their priority.
284
+ const mouseEnterEvent = document.createEvent('MouseEvents');
285
+ mouseEnterEvent.initEvent('mouseenter', true, true);
286
+ dispatchAndSetCurrentEvent(target.current, mouseEnterEvent);
287
+
288
+ // 3s should be enough to expire the updates
289
+ Scheduler.unstable_advanceTime(3000);
290
+ expect(Scheduler).toFlushExpired([]);
291
+ expect(container.textContent).toEqual('hovered');
292
+ });
293
+ });
294
});
packages/react-dom/src/events/DOMEventNames.js
+10
@@ -17,6 +17,8 @@ export type DOMEventName =
17
// 'animationend |
18
// 'animationstart' |
19
| 'beforeblur' // Not a real event. This is used by event experiments.
20
+ | 'beforeinput'
21
+ | 'blur'
22
| 'canplay'
23
| 'canplaythrough'
24
| 'cancel'
@@ -44,9 +46,12 @@ export type DOMEventName =
46
| 'encrypted'
47
| 'ended'
48
| 'error'
49
+ | 'focus'
50
| 'focusin'
51
| 'focusout'
52
+ | 'fullscreenchange'
53
| 'gotpointercapture'
54
+ | 'hashchange'
55
| 'input'
56
| 'invalid'
57
| 'keydown'
@@ -58,6 +63,8 @@ export type DOMEventName =
63
| 'loadedmetadata'
64
| 'lostpointercapture'
65
| 'mousedown'
66
+ | 'mouseenter'
67
+ | 'mouseleave'
68
| 'mousemove'
69
| 'mouseout'
70
| 'mouseover'
@@ -74,12 +81,15 @@ export type DOMEventName =
81
| 'pointerout'
82
| 'pointerover'
83
| 'pointerup'
84
+ | 'popstate'
85
| 'progress'
86
| 'ratechange'
87
| 'reset'
88
| 'scroll'
89
| 'seeked'
90
| 'seeking'
91
+ | 'select'
92
+ | 'selectstart'
93
| 'selectionchange'
94
| 'stalled'
95
| 'submit'
packages/react-dom/src/events/ReactDOMEventListener.js
+14
@@ -397,6 +397,16 @@ export function getEventPriority(domEventName: DOMEventName): * {
397
// eslint-disable-next-line no-fallthrough
398
case 'beforeblur':
399
case 'afterblur':
400
+ // Not used by React but could be by user code:
401
+ // eslint-disable-next-line no-fallthrough
402
+ case 'beforeinput':
403
+ case 'blur':
404
+ case 'fullscreenchange':
405
+ case 'focus':
406
+ case 'hashchange':
407
+ case 'popstate':
408
+ case 'select':
409
+ case 'selectstart':
410
return InputDiscreteLanePriority;
411
case 'drag':
412
case 'dragenter':
@@ -413,6 +423,10 @@ export function getEventPriority(domEventName: DOMEventName): * {
423
case 'toggle':
424
case 'touchmove':
425
case 'wheel':
426
+ // Not used by React but could be by user code:
427
+ // eslint-disable-next-line no-fallthrough
428
+ case 'mouseenter':
429
+ case 'mouseleave':
430
return InputContinuousLanePriority;
431
default:
432
return DefaultLanePriority;