Skip to content

Commit ad8211d

Browse files
authored
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
1 parent d919e2c commit ad8211d

File tree

3 files changed

+90
-0
lines changed

3 files changed

+90
-0
lines changed

packages/react-dom/src/__tests__/ReactDOMNativeEventHeuristic-test.js

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ let React;
1313

1414
let ReactDOM;
1515
let Scheduler;
16+
let act;
1617

1718
describe('ReactDOMNativeEventHeuristic-test', () => {
1819
let container;
@@ -23,6 +24,7 @@ describe('ReactDOMNativeEventHeuristic-test', () => {
2324
React = require('react');
2425
ReactDOM = require('react-dom');
2526
Scheduler = require('scheduler');
27+
act = require('react-dom/test-utils').unstable_concurrentAct;
2628

2729
document.body.appendChild(container);
2830
});
@@ -225,4 +227,68 @@ describe('ReactDOMNativeEventHeuristic-test', () => {
225227
// Therefore the form should have been submitted.
226228
expect(formSubmitted).toBe(true);
227229
});
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+
});
228294
});

packages/react-dom/src/events/DOMEventNames.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ export type DOMEventName =
1717
// 'animationend |
1818
// 'animationstart' |
1919
| 'beforeblur' // Not a real event. This is used by event experiments.
20+
| 'beforeinput'
21+
| 'blur'
2022
| 'canplay'
2123
| 'canplaythrough'
2224
| 'cancel'
@@ -44,9 +46,12 @@ export type DOMEventName =
4446
| 'encrypted'
4547
| 'ended'
4648
| 'error'
49+
| 'focus'
4750
| 'focusin'
4851
| 'focusout'
52+
| 'fullscreenchange'
4953
| 'gotpointercapture'
54+
| 'hashchange'
5055
| 'input'
5156
| 'invalid'
5257
| 'keydown'
@@ -58,6 +63,8 @@ export type DOMEventName =
5863
| 'loadedmetadata'
5964
| 'lostpointercapture'
6065
| 'mousedown'
66+
| 'mouseenter'
67+
| 'mouseleave'
6168
| 'mousemove'
6269
| 'mouseout'
6370
| 'mouseover'
@@ -74,12 +81,15 @@ export type DOMEventName =
7481
| 'pointerout'
7582
| 'pointerover'
7683
| 'pointerup'
84+
| 'popstate'
7785
| 'progress'
7886
| 'ratechange'
7987
| 'reset'
8088
| 'scroll'
8189
| 'seeked'
8290
| 'seeking'
91+
| 'select'
92+
| 'selectstart'
8393
| 'selectionchange'
8494
| 'stalled'
8595
| 'submit'

packages/react-dom/src/events/ReactDOMEventListener.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,16 @@ export function getEventPriority(domEventName: DOMEventName): * {
397397
// eslint-disable-next-line no-fallthrough
398398
case 'beforeblur':
399399
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':
400410
return InputDiscreteLanePriority;
401411
case 'drag':
402412
case 'dragenter':
@@ -413,6 +423,10 @@ export function getEventPriority(domEventName: DOMEventName): * {
413423
case 'toggle':
414424
case 'touchmove':
415425
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':
416430
return InputContinuousLanePriority;
417431
default:
418432
return DefaultLanePriority;

0 commit comments

Comments
 (0)