diff --git a/packages/react-dom/src/__tests__/ReactDOMServerSelectiveHydration-test.internal.js b/packages/react-dom/src/__tests__/ReactDOMServerSelectiveHydration-test.internal.js
index 6b543805ea102..19cf6065e6b9d 100644
--- a/packages/react-dom/src/__tests__/ReactDOMServerSelectiveHydration-test.internal.js
+++ b/packages/react-dom/src/__tests__/ReactDOMServerSelectiveHydration-test.internal.js
@@ -92,6 +92,21 @@ function dispatchClickEvent(target) {
return target.dispatchEvent(mouseOutEvent);
}
+// TODO: There's currently no React DOM API to opt into Idle priority updates,
+// and there's no native DOM event that maps to idle priority, so this is a
+// temporary workaround. Need something like ReactDOM.unstable_IdleUpdates.
+function TODO_scheduleIdleDOMSchedulerTask(fn) {
+ Scheduler.unstable_runWithPriority(Scheduler.unstable_IdlePriority, () => {
+ const prevEvent = window.event;
+ window.event = {type: 'message'};
+ try {
+ fn();
+ } finally {
+ window.event = prevEvent;
+ }
+ });
+}
+
describe('ReactDOMServerSelectiveHydration', () => {
beforeEach(() => {
jest.resetModuleRegistry();
@@ -889,12 +904,10 @@ describe('ReactDOMServerSelectiveHydration', () => {
expect(Scheduler).toFlushAndYieldThrough(['App', 'Commit']);
// Render an update at Idle priority that needs to update A.
- Scheduler.unstable_runWithPriority(
- Scheduler.unstable_IdlePriority,
- () => {
- root.render();
- },
- );
+
+ TODO_scheduleIdleDOMSchedulerTask(() => {
+ root.render();
+ });
// Start rendering. This will force the first boundary to hydrate
// by scheduling it at one higher pri than Idle.
diff --git a/packages/react-noop-renderer/src/ReactNoop.js b/packages/react-noop-renderer/src/ReactNoop.js
index 071501e576b6e..8305dd6d8641f 100644
--- a/packages/react-noop-renderer/src/ReactNoop.js
+++ b/packages/react-noop-renderer/src/ReactNoop.js
@@ -41,6 +41,7 @@ export const {
deferredUpdates,
unbatchedUpdates,
discreteUpdates,
+ idleUpdates,
flushDiscreteUpdates,
flushSync,
flushPassiveEffects,
diff --git a/packages/react-noop-renderer/src/ReactNoopPersistent.js b/packages/react-noop-renderer/src/ReactNoopPersistent.js
index 845c8d3acc1a3..c4a73cdfb81b4 100644
--- a/packages/react-noop-renderer/src/ReactNoopPersistent.js
+++ b/packages/react-noop-renderer/src/ReactNoopPersistent.js
@@ -41,6 +41,7 @@ export const {
deferredUpdates,
unbatchedUpdates,
discreteUpdates,
+ idleUpdates,
flushDiscreteUpdates,
flushSync,
flushPassiveEffects,
diff --git a/packages/react-noop-renderer/src/createReactNoop.js b/packages/react-noop-renderer/src/createReactNoop.js
index 34967ca3625ed..2aa0043d9e24a 100644
--- a/packages/react-noop-renderer/src/createReactNoop.js
+++ b/packages/react-noop-renderer/src/createReactNoop.js
@@ -27,6 +27,7 @@ import {
LegacyRoot,
} from 'react-reconciler/src/ReactRootTags';
+import {enableNativeEventPriorityInference} from 'shared/ReactFeatureFlags';
import ReactSharedInternals from 'shared/ReactSharedInternals';
import enqueueTask from 'shared/enqueueTask';
const {IsSomeRendererActing} = ReactSharedInternals;
@@ -392,7 +393,7 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
resetAfterCommit(): void {},
getCurrentEventPriority() {
- return NoopRenderer.DefaultEventPriority;
+ return currentEventPriority;
},
now: Scheduler.unstable_now,
@@ -587,6 +588,8 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
const roots = new Map();
const DEFAULT_ROOT_ID = '';
+ let currentEventPriority = NoopRenderer.DefaultEventPriority;
+
function childToJSX(child, text) {
if (text !== null) {
return text;
@@ -925,6 +928,23 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
discreteUpdates: NoopRenderer.discreteUpdates,
+ idleUpdates(fn: () => T): T {
+ if (enableNativeEventPriorityInference) {
+ const prevEventPriority = currentEventPriority;
+ currentEventPriority = NoopRenderer.IdleEventPriority;
+ try {
+ fn();
+ } finally {
+ currentEventPriority = prevEventPriority;
+ }
+ } else {
+ return Scheduler.unstable_runWithPriority(
+ Scheduler.unstable_IdlePriority,
+ fn,
+ );
+ }
+ },
+
flushDiscreteUpdates: NoopRenderer.flushDiscreteUpdates,
flushSync(fn: () => mixed) {
diff --git a/packages/react-reconciler/src/ReactFiberLane.new.js b/packages/react-reconciler/src/ReactFiberLane.new.js
index 315541115c37e..adb67b6e77dfe 100644
--- a/packages/react-reconciler/src/ReactFiberLane.new.js
+++ b/packages/react-reconciler/src/ReactFiberLane.new.js
@@ -70,7 +70,7 @@ const RetryLanePriority: LanePriority = 5;
const SelectiveHydrationLanePriority: LanePriority = 4;
const IdleHydrationLanePriority: LanePriority = 3;
-const IdleLanePriority: LanePriority = 2;
+export const IdleLanePriority: LanePriority = 2;
const OffscreenLanePriority: LanePriority = 1;
@@ -275,6 +275,7 @@ export function getNextLanes(root: FiberRoot, wipLanes: Lanes): Lanes {
// Check if any work has expired.
if (expiredLanes !== NoLanes) {
+ // TODO: Should entangle with SyncLane
nextLanes = expiredLanes;
nextLanePriority = return_highestLanePriority = SyncLanePriority;
} else {
diff --git a/packages/react-reconciler/src/ReactFiberLane.old.js b/packages/react-reconciler/src/ReactFiberLane.old.js
index e34c8ac0514fc..ea02f3102c902 100644
--- a/packages/react-reconciler/src/ReactFiberLane.old.js
+++ b/packages/react-reconciler/src/ReactFiberLane.old.js
@@ -70,7 +70,7 @@ const RetryLanePriority: LanePriority = 5;
const SelectiveHydrationLanePriority: LanePriority = 4;
const IdleHydrationLanePriority: LanePriority = 3;
-const IdleLanePriority: LanePriority = 2;
+export const IdleLanePriority: LanePriority = 2;
const OffscreenLanePriority: LanePriority = 1;
@@ -275,6 +275,7 @@ export function getNextLanes(root: FiberRoot, wipLanes: Lanes): Lanes {
// Check if any work has expired.
if (expiredLanes !== NoLanes) {
+ // TODO: Should entangle with SyncLane
nextLanes = expiredLanes;
nextLanePriority = return_highestLanePriority = SyncLanePriority;
} else {
diff --git a/packages/react-reconciler/src/ReactFiberReconciler.js b/packages/react-reconciler/src/ReactFiberReconciler.js
index 7493b73636a34..29b52c5a00092 100644
--- a/packages/react-reconciler/src/ReactFiberReconciler.js
+++ b/packages/react-reconciler/src/ReactFiberReconciler.js
@@ -55,6 +55,7 @@ import {
DefaultEventPriority as DefaultEventPriority_old,
DiscreteEventPriority as DiscreteEventPriority_old,
ContinuousEventPriority as ContinuousEventPriority_old,
+ IdleEventPriority as IdleEventPriority_old,
} from './ReactFiberReconciler.old';
import {
@@ -98,6 +99,7 @@ import {
DefaultEventPriority as DefaultEventPriority_new,
DiscreteEventPriority as DiscreteEventPriority_new,
ContinuousEventPriority as ContinuousEventPriority_new,
+ IdleEventPriority as IdleEventPriority_new,
} from './ReactFiberReconciler.new';
export const createContainer = enableNewReconciler
@@ -183,6 +185,9 @@ export const DiscreteEventPriority = enableNewReconciler
export const ContinuousEventPriority = enableNewReconciler
? ContinuousEventPriority_new
: ContinuousEventPriority_old;
+export const IdleEventPriority = enableNewReconciler
+ ? IdleEventPriority_new
+ : IdleEventPriority_old;
//TODO: "psuedo" is spelled "pseudo"
export const createHasPsuedoClassSelector = enableNewReconciler
diff --git a/packages/react-reconciler/src/ReactFiberReconciler.new.js b/packages/react-reconciler/src/ReactFiberReconciler.new.js
index 039bb159fd7a9..be5ee15970dc7 100644
--- a/packages/react-reconciler/src/ReactFiberReconciler.new.js
+++ b/packages/react-reconciler/src/ReactFiberReconciler.new.js
@@ -101,6 +101,7 @@ export {
InputDiscreteLanePriority as DiscreteEventPriority,
InputContinuousLanePriority as ContinuousEventPriority,
DefaultLanePriority as DefaultEventPriority,
+ IdleLanePriority as IdleEventPriority,
} from './ReactFiberLane.new';
export {registerMutableSourceForHydration} from './ReactMutableSource.new';
diff --git a/packages/react-reconciler/src/ReactFiberReconciler.old.js b/packages/react-reconciler/src/ReactFiberReconciler.old.js
index 012b4c7057350..9edb4e5031206 100644
--- a/packages/react-reconciler/src/ReactFiberReconciler.old.js
+++ b/packages/react-reconciler/src/ReactFiberReconciler.old.js
@@ -101,6 +101,7 @@ export {
InputDiscreteLanePriority as DiscreteEventPriority,
InputContinuousLanePriority as ContinuousEventPriority,
DefaultLanePriority as DefaultEventPriority,
+ IdleLanePriority as IdleEventPriority,
} from './ReactFiberLane.old';
export {registerMutableSourceForHydration} from './ReactMutableSource.new';
diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js
index e65e1dad45d6e..234ca61fe93f1 100644
--- a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js
+++ b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js
@@ -465,15 +465,7 @@ export function requestUpdateLane(fiber: Fiber): Lane {
} else {
if (enableNativeEventPriorityInference) {
const eventLanePriority = getCurrentEventPriority();
- if (eventLanePriority === DefaultLanePriority) {
- // TODO: move this case into the ReactDOM host config.
- const schedulerLanePriority = schedulerPriorityToLanePriority(
- schedulerPriority,
- );
- lane = findUpdateLane(schedulerLanePriority, currentEventWipLanes);
- } else {
- lane = findUpdateLane(eventLanePriority, currentEventWipLanes);
- }
+ lane = findUpdateLane(eventLanePriority, currentEventWipLanes);
} else {
const schedulerLanePriority = schedulerPriorityToLanePriority(
schedulerPriority,
diff --git a/packages/react-reconciler/src/__tests__/ReactExpiration-test.js b/packages/react-reconciler/src/__tests__/ReactExpiration-test.js
index 590761dbc8cd7..017c712bd814b 100644
--- a/packages/react-reconciler/src/__tests__/ReactExpiration-test.js
+++ b/packages/react-reconciler/src/__tests__/ReactExpiration-test.js
@@ -403,23 +403,23 @@ describe('ReactExpiration', () => {
expect(ReactNoop).toMatchRenderedOutput('Hi');
});
- it('prevents starvation by high priority updates', async () => {
+ it('prevents starvation by sync updates', async () => {
const {useState} = React;
- let updateHighPri;
+ let updateSyncPri;
let updateNormalPri;
function App() {
const [highPri, setHighPri] = useState(0);
const [normalPri, setNormalPri] = useState(0);
- updateHighPri = () =>
- Scheduler.unstable_runWithPriority(
- Scheduler.unstable_UserBlockingPriority,
- () => setHighPri(n => n + 1),
- );
+ updateSyncPri = () => {
+ ReactNoop.flushSync(() => {
+ setHighPri(n => n + 1);
+ });
+ };
updateNormalPri = () => setNormalPri(n => n + 1);
return (
<>
-
+
{', '}
>
@@ -430,29 +430,29 @@ describe('ReactExpiration', () => {
await ReactNoop.act(async () => {
root.render();
});
- expect(Scheduler).toHaveYielded(['High pri: 0', 'Normal pri: 0']);
- expect(root).toMatchRenderedOutput('High pri: 0, Normal pri: 0');
+ expect(Scheduler).toHaveYielded(['Sync pri: 0', 'Normal pri: 0']);
+ expect(root).toMatchRenderedOutput('Sync pri: 0, Normal pri: 0');
// First demonstrate what happens when there's no starvation
await ReactNoop.act(async () => {
updateNormalPri();
- expect(Scheduler).toFlushAndYieldThrough(['High pri: 0']);
- updateHighPri();
+ expect(Scheduler).toFlushAndYieldThrough(['Sync pri: 0']);
+ updateSyncPri();
});
expect(Scheduler).toHaveYielded([
// Interrupt high pri update to render sync update
- 'High pri: 1',
+ 'Sync pri: 1',
'Normal pri: 0',
// Now render normal pri
- 'High pri: 1',
+ 'Sync pri: 1',
'Normal pri: 1',
]);
- expect(root).toMatchRenderedOutput('High pri: 1, Normal pri: 1');
+ expect(root).toMatchRenderedOutput('Sync pri: 1, Normal pri: 1');
// Do the same thing, but starve the first update
await ReactNoop.act(async () => {
updateNormalPri();
- expect(Scheduler).toFlushAndYieldThrough(['High pri: 1']);
+ expect(Scheduler).toFlushAndYieldThrough(['Sync pri: 1']);
// This time, a lot of time has elapsed since the normal pri update
// started rendering. (This should advance time by some number that's
@@ -461,86 +461,16 @@ describe('ReactExpiration', () => {
Scheduler.unstable_advanceTime(10000);
// So when we get a high pri update, we shouldn't interrupt
- updateHighPri();
+ updateSyncPri();
});
expect(Scheduler).toHaveYielded([
// Finish normal pri update
'Normal pri: 2',
// Then do high pri update
- 'High pri: 2',
- 'Normal pri: 2',
- ]);
- expect(root).toMatchRenderedOutput('High pri: 2, Normal pri: 2');
- });
-
- it('prevents starvation by sync updates', async () => {
- const {useState} = React;
-
- let updateSyncPri;
- let updateHighPri;
- function App() {
- const [syncPri, setSyncPri] = useState(0);
- const [highPri, setHighPri] = useState(0);
- updateSyncPri = () => ReactNoop.flushSync(() => setSyncPri(n => n + 1));
- updateHighPri = () =>
- Scheduler.unstable_runWithPriority(
- Scheduler.unstable_UserBlockingPriority,
- () => setHighPri(n => n + 1),
- );
- return (
- <>
-
- {', '}
-
- >
- );
- }
-
- const root = ReactNoop.createRoot();
- await ReactNoop.act(async () => {
- root.render();
- });
- expect(Scheduler).toHaveYielded(['Sync pri: 0', 'High pri: 0']);
- expect(root).toMatchRenderedOutput('Sync pri: 0, High pri: 0');
-
- // First demonstrate what happens when there's no starvation
- await ReactNoop.act(async () => {
- updateHighPri();
- expect(Scheduler).toFlushAndYieldThrough(['Sync pri: 0']);
- updateSyncPri();
- });
- expect(Scheduler).toHaveYielded([
- // Interrupt high pri update to render sync update
- 'Sync pri: 1',
- 'High pri: 0',
- // Now render high pri
- 'Sync pri: 1',
- 'High pri: 1',
- ]);
- expect(root).toMatchRenderedOutput('Sync pri: 1, High pri: 1');
-
- // Do the same thing, but starve the first update
- await ReactNoop.act(async () => {
- updateHighPri();
- expect(Scheduler).toFlushAndYieldThrough(['Sync pri: 1']);
-
- // This time, a lot of time has elapsed since the high pri update started
- // rendering. (This should advance time by some number that's definitely
- // bigger than the constant heuristic we use to detect starvation of user
- // interactions, but not as high as the onse used for normal pri updates.)
- Scheduler.unstable_advanceTime(1500);
-
- // So when we get a sync update, we shouldn't interrupt
- updateSyncPri();
- });
- expect(Scheduler).toHaveYielded([
- // Finish high pri update
- 'High pri: 2',
- // Then do sync update
'Sync pri: 2',
- 'High pri: 2',
+ 'Normal pri: 2',
]);
- expect(root).toMatchRenderedOutput('Sync pri: 2, High pri: 2');
+ expect(root).toMatchRenderedOutput('Sync pri: 2, Normal pri: 2');
});
it('idle work never expires', async () => {
@@ -553,10 +483,9 @@ describe('ReactExpiration', () => {
const [highPri, setIdlePri] = useState(0);
updateSyncPri = () => ReactNoop.flushSync(() => setSyncPri(n => n + 1));
updateIdlePri = () =>
- Scheduler.unstable_runWithPriority(
- Scheduler.unstable_IdlePriority,
- () => setIdlePri(n => n + 1),
- );
+ ReactNoop.idleUpdates(() => {
+ setIdlePri(n => n + 1);
+ });
return (
<>
@@ -695,11 +624,11 @@ describe('ReactExpiration', () => {
function App() {
const [highPri, setHighPri] = useState(0);
const [normalPri, setNormalPri] = useState(0);
- updateHighPri = () =>
- Scheduler.unstable_runWithPriority(
- Scheduler.unstable_UserBlockingPriority,
- () => setHighPri(n => n + 1),
- );
+ updateHighPri = () => {
+ ReactNoop.flushSync(() => {
+ setHighPri(n => n + 1);
+ });
+ };
updateNormalPri = () => setNormalPri(n => n + 1);
return (
<>
@@ -735,20 +664,34 @@ describe('ReactExpiration', () => {
expect(Scheduler).toFlushAndYieldThrough(['Normal pri: 1']);
// More time goes by. This expires both of the updates just scheduled.
Scheduler.unstable_advanceTime(10000);
+ expect(Scheduler).toHaveYielded([]);
// Attempt to interrupt with a high pri update.
updateHighPri();
// Both normal pri updates should have expired.
- expect(Scheduler).toFlushExpired([
- 'Sibling',
- // Notice that the high pri update didn't flush yet. Expiring one lane
- // doesn't affect other lanes. (Unless they are intentionally entangled,
- // like we do for overlapping transitions that affect the same state.)
- 'High pri: 0',
- 'Normal pri: 2',
- 'Sibling',
- ]);
+ if (gate(flags => flags.FIXME)) {
+ // The sync update and the expired normal pri updates render in a
+ // single batch.
+ expect(Scheduler).toHaveYielded([
+ 'Sibling',
+ 'High pri: 1',
+ 'Normal pri: 2',
+ 'Sibling',
+ ]);
+ } else {
+ expect(Scheduler).toHaveYielded([
+ 'Sibling',
+ 'High pri: 0',
+ 'Normal pri: 2',
+ 'Sibling',
+ // TODO: This is the sync update. We should have rendered it in the same
+ // batch as the expired update.
+ 'High pri: 1',
+ 'Normal pri: 2',
+ 'Sibling',
+ ]);
+ }
});
});
diff --git a/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js b/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js
index cc86edb132209..bf5503feb4902 100644
--- a/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js
+++ b/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js
@@ -1351,11 +1351,10 @@ describe('ReactHooksWithNoopRenderer', () => {
expect(Scheduler).toFlushAndYieldThrough(['Child one render']);
// Schedule unmount for the parent that unmounts children with pending update.
- Scheduler.unstable_runWithPriority(
- Scheduler.unstable_UserBlockingPriority,
- () => setParentState(false),
- );
- expect(Scheduler).toFlushAndYieldThrough([
+ ReactNoop.flushSync(() => {
+ setParentState(false);
+ });
+ expect(Scheduler).toHaveYielded([
'Parent false render',
'Parent false commit',
]);
diff --git a/packages/react-reconciler/src/__tests__/ReactSchedulerIntegration-test.js b/packages/react-reconciler/src/__tests__/ReactSchedulerIntegration-test.js
index 95c721b87a7c3..4292333293e82 100644
--- a/packages/react-reconciler/src/__tests__/ReactSchedulerIntegration-test.js
+++ b/packages/react-reconciler/src/__tests__/ReactSchedulerIntegration-test.js
@@ -79,6 +79,10 @@ describe('ReactSchedulerIntegration', () => {
expect(Scheduler).toHaveYielded(['Priority: Immediate']);
});
+ // TODO: Figure out what to do with these tests. I don't think most of them
+ // make sense once we decouple Scheduler from React. Perhaps need similar
+ // tests for React DOM.
+ // @gate !enableNativeEventPriorityInference
it('has correct priority during rendering', () => {
function ReadPriority() {
Scheduler.unstable_yieldValue(
@@ -100,6 +104,10 @@ describe('ReactSchedulerIntegration', () => {
expect(Scheduler).toFlushAndYield(['Priority: Idle']);
});
+ // TODO: Figure out what to do with these tests. I don't think most of them
+ // make sense once we decouple Scheduler from React. Perhaps need similar
+ // tests for React DOM.
+ // @gate !enableNativeEventPriorityInference
it('has correct priority when continuing a render after yielding', () => {
function ReadPriority() {
Scheduler.unstable_yieldValue(
@@ -152,6 +160,10 @@ describe('ReactSchedulerIntegration', () => {
]);
});
+ // TODO: Figure out what to do with these tests. I don't think most of them
+ // make sense once we decouple Scheduler from React. Perhaps need similar
+ // tests for React DOM.
+ // @gate !enableNativeEventPriorityInference
it('passive effects never have higher than normal priority', async () => {
const {useEffect} = React;
function ReadPriority({step}) {
@@ -205,6 +217,10 @@ describe('ReactSchedulerIntegration', () => {
]);
});
+ // TODO: Figure out what to do with these tests. I don't think most of them
+ // make sense once we decouple Scheduler from React. Perhaps need similar
+ // tests for React DOM.
+ // @gate !enableNativeEventPriorityInference
it('passive effects have correct priority even if they are flushed early', async () => {
const {useEffect} = React;
function ReadPriority({step}) {
@@ -233,6 +249,10 @@ describe('ReactSchedulerIntegration', () => {
]);
});
+ // TODO: Figure out what to do with these tests. I don't think most of them
+ // make sense once we decouple Scheduler from React. Perhaps need similar
+ // tests for React DOM.
+ // @gate !enableNativeEventPriorityInference
it('passive effect clean-up functions have correct priority even when component is deleted', async () => {
const {useEffect} = React;
function ReadPriority({step}) {
@@ -322,6 +342,10 @@ describe('ReactSchedulerIntegration', () => {
]);
});
+ // TODO: Figure out what to do with these tests. I don't think most of them
+ // make sense once we decouple Scheduler from React. Perhaps need similar
+ // tests for React DOM.
+ // @gate !enableNativeEventPriorityInference
it('after completing a level of work, infers priority of the next batch based on its expiration time', () => {
function App({label}) {
Scheduler.unstable_yieldValue(
diff --git a/packages/react-reconciler/src/__tests__/ReactSuspenseWithNoopRenderer-test.js b/packages/react-reconciler/src/__tests__/ReactSuspenseWithNoopRenderer-test.js
index 18042bb4bfb55..901be8f1a6ad8 100644
--- a/packages/react-reconciler/src/__tests__/ReactSuspenseWithNoopRenderer-test.js
+++ b/packages/react-reconciler/src/__tests__/ReactSuspenseWithNoopRenderer-test.js
@@ -2248,9 +2248,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
expect(ReactNoop.getChildren()).toEqual([]);
// Schedule an update at idle pri.
- Scheduler.unstable_runWithPriority(Scheduler.unstable_IdlePriority, () =>
- ReactNoop.render(),
- );
+ ReactNoop.idleUpdates(() => ReactNoop.render());
// We won't even work on Idle priority.
expect(Scheduler).toFlushAndYield([]);
@@ -3018,12 +3016,9 @@ describe('ReactSuspenseWithNoopRenderer', () => {
setText('B');
await resolveText('C');
- Scheduler.unstable_runWithPriority(
- Scheduler.unstable_IdlePriority,
- () => {
- setText('C');
- },
- );
+ ReactNoop.idleUpdates(() => {
+ setText('C');
+ });
expect(Scheduler).toFlushAndYield([
// First we attempt the high pri update. It suspends.
@@ -3282,12 +3277,9 @@ describe('ReactSuspenseWithNoopRenderer', () => {
// And another update at lower priority. This will unblock.
await resolveText('E');
- Scheduler.unstable_runWithPriority(
- Scheduler.unstable_IdlePriority,
- () => {
- setText('E');
- },
- );
+ ReactNoop.idleUpdates(() => {
+ setText('E');
+ });
});
// Even though the fragment fiber is not part of the return path, we should
// be able to finish rendering.
@@ -3838,12 +3830,9 @@ describe('ReactSuspenseWithNoopRenderer', () => {
await ReactNoop.act(async () => {
setText('B');
- Scheduler.unstable_runWithPriority(
- Scheduler.unstable_IdlePriority,
- () => {
- setText('B');
- },
- );
+ ReactNoop.idleUpdates(() => {
+ setText('B');
+ });
// Suspend the first update. The second update doesn't run because it has
// Idle priority.
expect(Scheduler).toFlushAndYield(['Suspend! [B]', 'Loading...']);
diff --git a/packages/react-reconciler/src/__tests__/useMutableSource-test.internal.js b/packages/react-reconciler/src/__tests__/useMutableSource-test.internal.js
index 1969c2f7adce4..db4214bfd0656 100644
--- a/packages/react-reconciler/src/__tests__/useMutableSource-test.internal.js
+++ b/packages/react-reconciler/src/__tests__/useMutableSource-test.internal.js
@@ -529,7 +529,7 @@ describe('useMutableSource', () => {
// Changing values should schedule an update with React.
// Start working on this update but don't finish it.
- Scheduler.unstable_runWithPriority(Scheduler.unstable_LowPriority, () => {
+ ReactNoop.idleUpdates(() => {
source.value = 'two';
expect(Scheduler).toFlushAndYieldThrough(['a:two']);
});
@@ -538,29 +538,26 @@ describe('useMutableSource', () => {
// Force a higher priority render with a new config.
// This should signal that the snapshot is not safe and trigger a full re-render.
- Scheduler.unstable_runWithPriority(
- Scheduler.unstable_UserBlockingPriority,
- () => {
- ReactNoop.render(
- <>
-
-
- >,
- () => Scheduler.unstable_yieldValue('Sync effect'),
- );
- },
- );
- expect(Scheduler).toFlushAndYieldThrough([
+ ReactNoop.flushSync(() => {
+ ReactNoop.render(
+ <>
+
+
+ >,
+ () => Scheduler.unstable_yieldValue('Sync effect'),
+ );
+ });
+ expect(Scheduler).toHaveYielded([
'a:new:two',
'b:new:two',
'Sync effect',
@@ -596,7 +593,7 @@ describe('useMutableSource', () => {
// Changing values should schedule an update with React.
// Start working on this update but don't finish it.
- Scheduler.unstable_runWithPriority(Scheduler.unstable_LowPriority, () => {
+ ReactNoop.idleUpdates(() => {
source.value = 'two';
expect(Scheduler).toFlushAndYieldThrough(['a:two']);
});
@@ -793,19 +790,14 @@ describe('useMutableSource', () => {
ReactNoop.flushPassiveEffects();
// Change the source (and schedule an update).
- Scheduler.unstable_runWithPriority(Scheduler.unstable_LowPriority, () => {
- source.value = 'two';
- });
+ source.value = 'two';
// Schedule a higher priority update that changes getSnapshot.
- Scheduler.unstable_runWithPriority(
- Scheduler.unstable_UserBlockingPriority,
- () => {
- updateGetSnapshot(() => newGetSnapshot);
- },
- );
+ ReactNoop.flushSync(() => {
+ updateGetSnapshot(() => newGetSnapshot);
+ });
- expect(Scheduler).toFlushAndYield(['only:new:two']);
+ expect(Scheduler).toHaveYielded(['only:new:two']);
});
});
diff --git a/packages/react-reconciler/src/__tests__/useMutableSourceHydration-test.js b/packages/react-reconciler/src/__tests__/useMutableSourceHydration-test.js
index 2cd6881c94f73..6afa7588bbfb0 100644
--- a/packages/react-reconciler/src/__tests__/useMutableSourceHydration-test.js
+++ b/packages/react-reconciler/src/__tests__/useMutableSourceHydration-test.js
@@ -31,6 +31,15 @@ describe('useMutableSourceHydration', () => {
useMutableSource = React.unstable_useMutableSource;
});
+ function dispatchAndSetCurrentEvent(el, event) {
+ try {
+ window.event = event;
+ el.dispatchEvent(event);
+ } finally {
+ window.event = undefined;
+ }
+ }
+
const defaultGetSnapshot = source => source.value;
const defaultSubscribe = (source, callback) => source.subscribe(callback);
@@ -332,6 +341,7 @@ describe('useMutableSourceHydration', () => {
});
// @gate experimental
+ // @gate enableNativeEventPriorityInference
it('should detect a tear during a higher priority interruption', () => {
const source = createSource('one');
const mutableSource = createMutableSource(source, param => param.version);
@@ -371,16 +381,22 @@ describe('useMutableSourceHydration', () => {
mutableSources: [mutableSource],
},
});
+
expect(() => {
act(() => {
root.render();
expect(Scheduler).toFlushAndYieldThrough([1]);
// Render an update which will be higher priority than the hydration.
- Scheduler.unstable_runWithPriority(
- Scheduler.unstable_UserBlockingPriority,
- () => root.render(),
- );
+ // We can do this by scheduling the update inside a mouseover event.
+ const arbitraryElement = document.createElement('div');
+ const mouseOverEvent = document.createEvent('MouseEvents');
+ mouseOverEvent.initEvent('mouseover', true, true);
+ arbitraryElement.addEventListener('mouseover', () => {
+ root.render();
+ });
+ dispatchAndSetCurrentEvent(arbitraryElement, mouseOverEvent);
+
expect(Scheduler).toFlushAndYieldThrough([2]);
source.value = 'two';
diff --git a/packages/react/src/__tests__/ReactDOMTracing-test.internal.js b/packages/react/src/__tests__/ReactDOMTracing-test.internal.js
index bd097fea76548..e2a76513f721c 100644
--- a/packages/react/src/__tests__/ReactDOMTracing-test.internal.js
+++ b/packages/react/src/__tests__/ReactDOMTracing-test.internal.js
@@ -28,6 +28,7 @@ let onWorkStopped;
// This is hard coded directly to avoid needing to import, and
// we'll remove this as we replace runWithPriority with React APIs.
const IdleLanePriority = 2;
+const InputContinuousPriority = 10;
function loadModules() {
ReactFeatureFlags = require('shared/ReactFeatureFlags');
@@ -427,6 +428,7 @@ describe('ReactDOMTracing', () => {
});
// @gate experimental
+ // @gate enableNativeEventPriorityInference
it('should properly trace interactions when there is work of interleaved priorities', () => {
const Child = () => {
Scheduler.unstable_yieldValue('Child');
@@ -502,9 +504,8 @@ describe('ReactDOMTracing', () => {
let interaction = null;
SchedulerTracing.unstable_trace('update', 0, () => {
interaction = Array.from(SchedulerTracing.unstable_getCurrent())[0];
- Scheduler.unstable_runWithPriority(
- Scheduler.unstable_UserBlockingPriority,
- () => scheduleUpdateWithHidden(),
+ ReactDOM.unstable_runWithPriority(InputContinuousPriority, () =>
+ scheduleUpdateWithHidden(),
);
});
scheduleUpdate();
@@ -549,6 +550,7 @@ describe('ReactDOMTracing', () => {
});
// @gate experimental
+ // @gate enableNativeEventPriorityInference
it('should properly trace interactions through a multi-pass SuspenseList render', () => {
const SuspenseList = React.SuspenseList;
const Suspense = React.Suspense;
@@ -610,10 +612,9 @@ describe('ReactDOMTracing', () => {
// Schedule an unrelated low priority update that shouldn't be included
// in the previous interaction. This is meant to ensure that we don't
// rely on the whole tree completing to cover up bugs.
- Scheduler.unstable_runWithPriority(
- Scheduler.unstable_IdlePriority,
- () => root.render(),
- );
+ ReactDOM.unstable_runWithPriority(IdleLanePriority, () => {
+ root.render();
+ });
expect(onInteractionTraced).toHaveBeenCalledTimes(1);
expect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(