@@ -12,12 +12,16 @@ import type {Batch, FiberRoot} from './ReactFiberRoot';
12
12
import type { ExpirationTime } from './ReactFiberExpirationTime' ;
13
13
import type { Interaction } from 'scheduler/src/Tracing' ;
14
14
15
+ // Intentionally not named imports because Rollup would use dynamic dispatch for
16
+ // CommonJS interop named imports.
17
+ // TODO: We're not using this import anymore, but I've left this here so we
18
+ // don't accidentally use named imports when we add it back.
19
+ // import * as Scheduler from 'scheduler';
15
20
import {
16
21
__interactionsRef ,
17
22
__subscriberRef ,
18
23
unstable_wrap as Scheduler_tracing_wrap ,
19
24
} from 'scheduler/tracing' ;
20
- import * as Scheduler from 'scheduler' ;
21
25
import {
22
26
invokeGuardedCallback ,
23
27
hasCaughtError ,
@@ -126,7 +130,7 @@ import {
126
130
computeAsyncExpiration ,
127
131
computeInteractiveExpiration ,
128
132
} from './ReactFiberExpirationTime' ;
129
- import { ConcurrentMode , ProfileMode , NoContext } from './ReactTypeOfMode' ;
133
+ import { ConcurrentMode , ProfileMode } from './ReactTypeOfMode' ;
130
134
import { enqueueUpdate , resetCurrentlyProcessingQueue } from './ReactUpdateQueue' ;
131
135
import { createCapturedValue } from './ReactCapturedValue' ;
132
136
import {
@@ -172,19 +176,6 @@ export type Thenable = {
172
176
then ( resolve : ( ) => mixed , reject ?: ( ) => mixed ) : mixed ,
173
177
} ;
174
178
175
- // Intentionally not named imports because Rollup would
176
- // use dynamic dispatch for CommonJS interop named imports.
177
- const {
178
- unstable_next : Scheduler_next ,
179
- unstable_getCurrentPriorityLevel : getCurrentPriorityLevel ,
180
- unstable_runWithPriority : runWithPriority ,
181
- unstable_ImmediatePriority : ImmediatePriority ,
182
- unstable_UserBlockingPriority : UserBlockingPriority ,
183
- unstable_NormalPriority : NormalPriority ,
184
- unstable_LowPriority : LowPriority ,
185
- unstable_IdlePriority : IdlePriority ,
186
- } = Scheduler ;
187
-
188
179
const { ReactCurrentDispatcher, ReactCurrentOwner} = ReactSharedInternals ;
189
180
190
181
let didWarnAboutStateTransition ;
@@ -259,6 +250,11 @@ if (__DEV__) {
259
250
// Used to ensure computeUniqueAsyncExpiration is monotonically decreasing.
260
251
let lastUniqueAsyncExpiration : number = Sync - 1 ;
261
252
253
+ // Represents the expiration time that incoming updates should use. (If this
254
+ // is NoWork, use the default strategy: async updates in async mode, sync
255
+ // updates in sync mode.)
256
+ let expirationContext : ExpirationTime = NoWork ;
257
+
262
258
let isWorking : boolean = false ;
263
259
264
260
// The next work in progress fiber that we're currently working on.
@@ -811,9 +807,7 @@ function commitRoot(root: FiberRoot, finishedWork: Fiber): void {
811
807
// here because that code is still in flux.
812
808
callback = Scheduler_tracing_wrap ( callback ) ;
813
809
}
814
- passiveEffectCallbackHandle = runWithPriority ( NormalPriority , ( ) => {
815
- return schedulePassiveEffects ( callback ) ;
816
- } ) ;
810
+ passiveEffectCallbackHandle = schedulePassiveEffects ( callback ) ;
817
811
passiveEffectCallback = callback ;
818
812
}
819
813
@@ -1597,58 +1591,52 @@ function computeUniqueAsyncExpiration(): ExpirationTime {
1597
1591
}
1598
1592
1599
1593
function computeExpirationForFiber ( currentTime : ExpirationTime , fiber : Fiber ) {
1600
- const priorityLevel = getCurrentPriorityLevel ( ) ;
1601
-
1602
1594
let expirationTime ;
1603
- if ( ( fiber . mode & ConcurrentMode ) === NoContext ) {
1604
- // Outside of concurrent mode, updates are always synchronous.
1605
- expirationTime = Sync ;
1606
- } else if ( isWorking && ! isCommitting ) {
1607
- // During render phase, updates expire during as the current render.
1608
- expirationTime = nextRenderExpirationTime ;
1595
+ if ( expirationContext !== NoWork ) {
1596
+ // An explicit expiration context was set;
1597
+ expirationTime = expirationContext ;
1598
+ } else if ( isWorking ) {
1599
+ if ( isCommitting ) {
1600
+ // Updates that occur during the commit phase should have sync priority
1601
+ // by default.
1602
+ expirationTime = Sync ;
1603
+ } else {
1604
+ // Updates during the render phase should expire at the same time as
1605
+ // the work that is being rendered.
1606
+ expirationTime = nextRenderExpirationTime ;
1607
+ }
1609
1608
} else {
1610
- switch ( priorityLevel ) {
1611
- case ImmediatePriority :
1612
- expirationTime = Sync ;
1613
- break ;
1614
- case UserBlockingPriority :
1609
+ // No explicit expiration context was set, and we're not currently
1610
+ // performing work. Calculate a new expiration time.
1611
+ if ( fiber . mode & ConcurrentMode ) {
1612
+ if ( isBatchingInteractiveUpdates ) {
1613
+ // This is an interactive update
1615
1614
expirationTime = computeInteractiveExpiration ( currentTime ) ;
1616
- break ;
1617
- case NormalPriority :
1618
- // This is a normal, concurrent update
1615
+ } else {
1616
+ // This is an async update
1619
1617
expirationTime = computeAsyncExpiration ( currentTime ) ;
1620
- break ;
1621
- case LowPriority :
1622
- case IdlePriority :
1623
- expirationTime = Never ;
1624
- break ;
1625
- default :
1626
- invariant (
1627
- false ,
1628
- 'Unknown priority level. This error is likely caused by a bug in ' +
1629
- 'React. Please file an issue.' ,
1630
- ) ;
1631
- }
1632
-
1633
- // If we're in the middle of rendering a tree, do not update at the same
1634
- // expiration time that is already rendering.
1635
- if ( nextRoot !== null && expirationTime === nextRenderExpirationTime ) {
1636
- expirationTime -= 1 ;
1618
+ }
1619
+ // If we're in the middle of rendering a tree, do not update at the same
1620
+ // expiration time that is already rendering.
1621
+ if ( nextRoot !== null && expirationTime === nextRenderExpirationTime ) {
1622
+ expirationTime -= 1 ;
1623
+ }
1624
+ } else {
1625
+ // This is a sync update
1626
+ expirationTime = Sync ;
1637
1627
}
1638
1628
}
1639
-
1640
- // Keep track of the lowest pending interactive expiration time. This
1641
- // allows us to synchronously flush all interactive updates
1642
- // when needed.
1643
- // TODO: Move this to renderer?
1644
- if (
1645
- priorityLevel === UserBlockingPriority &&
1646
- ( lowestPriorityPendingInteractiveExpirationTime === NoWork ||
1647
- expirationTime < lowestPriorityPendingInteractiveExpirationTime )
1648
- ) {
1649
- lowestPriorityPendingInteractiveExpirationTime = expirationTime ;
1629
+ if ( isBatchingInteractiveUpdates ) {
1630
+ // This is an interactive update. Keep track of the lowest pending
1631
+ // interactive expiration time. This allows us to synchronously flush
1632
+ // all interactive updates when needed.
1633
+ if (
1634
+ lowestPriorityPendingInteractiveExpirationTime === NoWork ||
1635
+ expirationTime < lowestPriorityPendingInteractiveExpirationTime
1636
+ ) {
1637
+ lowestPriorityPendingInteractiveExpirationTime = expirationTime ;
1638
+ }
1650
1639
}
1651
-
1652
1640
return expirationTime ;
1653
1641
}
1654
1642
@@ -1909,16 +1897,34 @@ function scheduleWork(fiber: Fiber, expirationTime: ExpirationTime) {
1909
1897
}
1910
1898
}
1911
1899
1900
+ function deferredUpdates < A > (fn: () => A) : A {
1901
+ const currentTime = requestCurrentTime ( ) ;
1902
+ const previousExpirationContext = expirationContext ;
1903
+ const previousIsBatchingInteractiveUpdates = isBatchingInteractiveUpdates ;
1904
+ expirationContext = computeAsyncExpiration ( currentTime ) ;
1905
+ isBatchingInteractiveUpdates = false ;
1906
+ try {
1907
+ return fn ( ) ;
1908
+ } finally {
1909
+ expirationContext = previousExpirationContext ;
1910
+ isBatchingInteractiveUpdates = previousIsBatchingInteractiveUpdates ;
1911
+ }
1912
+ }
1913
+
1912
1914
function syncUpdates < A , B , C0 , D , R > (
1913
1915
fn : ( A , B , C0 , D ) = > R ,
1914
1916
a : A ,
1915
1917
b : B ,
1916
1918
c : C0 ,
1917
1919
d : D ,
1918
1920
) : R {
1919
- return runWithPriority ( ImmediatePriority , ( ) => {
1921
+ const previousExpirationContext = expirationContext ;
1922
+ expirationContext = Sync ;
1923
+ try {
1920
1924
return fn ( a , b , c , d ) ;
1921
- } ) ;
1925
+ } finally {
1926
+ expirationContext = previousExpirationContext ;
1927
+ }
1922
1928
}
1923
1929
1924
1930
// TODO: Everything below this is written as if it has been lifted to the
@@ -1939,6 +1945,7 @@ let unhandledError: mixed | null = null;
1939
1945
1940
1946
let isBatchingUpdates : boolean = false ;
1941
1947
let isUnbatchingUpdates : boolean = false ;
1948
+ let isBatchingInteractiveUpdates : boolean = false ;
1942
1949
1943
1950
let completedBatches : Array < Batch > | null = null ;
1944
1951
@@ -2450,9 +2457,7 @@ function completeRoot(
2450
2457
lastCommittedRootDuringThisBatch = root ;
2451
2458
nestedUpdateCount = 0 ;
2452
2459
}
2453
- runWithPriority ( ImmediatePriority , ( ) => {
2454
- commitRoot ( root , finishedWork ) ;
2455
- } ) ;
2460
+ commitRoot ( root , finishedWork ) ;
2456
2461
}
2457
2462
2458
2463
function onUncaughtError ( error : mixed ) {
@@ -2518,6 +2523,9 @@ function flushSync<A, R>(fn: (a: A) => R, a: A): R {
2518
2523
}
2519
2524
2520
2525
function interactiveUpdates < A , B , R > ( fn : ( A , B ) = > R , a : A , b : B ) : R {
2526
+ if ( isBatchingInteractiveUpdates ) {
2527
+ return fn ( a , b ) ;
2528
+ }
2521
2529
// If there are any pending interactive updates, synchronously flush them.
2522
2530
// This needs to happen before we read any handlers, because the effect of
2523
2531
// the previous event may influence which handlers are called during
@@ -2531,13 +2539,14 @@ function interactiveUpdates<A, B, R>(fn: (A, B) => R, a: A, b: B): R {
2531
2539
performWork ( lowestPriorityPendingInteractiveExpirationTime , false ) ;
2532
2540
lowestPriorityPendingInteractiveExpirationTime = NoWork ;
2533
2541
}
2542
+ const previousIsBatchingInteractiveUpdates = isBatchingInteractiveUpdates ;
2534
2543
const previousIsBatchingUpdates = isBatchingUpdates ;
2544
+ isBatchingInteractiveUpdates = true ;
2535
2545
isBatchingUpdates = true ;
2536
2546
try {
2537
- return runWithPriority ( UserBlockingPriority , ( ) => {
2538
- return fn ( a , b ) ;
2539
- } ) ;
2547
+ return fn ( a , b ) ;
2540
2548
} finally {
2549
+ isBatchingInteractiveUpdates = previousIsBatchingInteractiveUpdates ;
2541
2550
isBatchingUpdates = previousIsBatchingUpdates ;
2542
2551
if ( ! isBatchingUpdates && ! isRendering ) {
2543
2552
performSyncWork ( ) ;
@@ -2588,7 +2597,7 @@ export {
2588
2597
unbatchedUpdates,
2589
2598
flushSync,
2590
2599
flushControlled,
2591
- Scheduler_next as deferredUpdates,
2600
+ deferredUpdates,
2592
2601
syncUpdates,
2593
2602
interactiveUpdates,
2594
2603
flushInteractiveUpdates,
0 commit comments