@@ -15,18 +15,8 @@ import type {Interaction} from 'scheduler/src/Tracing';
15
15
import {
16
16
__interactionsRef ,
17
17
__subscriberRef ,
18
- unstable_wrap as Scheduler_tracing_wrap ,
18
+ unstable_wrap as Schedule_tracing_wrap ,
19
19
} from 'scheduler/tracing' ;
20
- import {
21
- unstable_next as Scheduler_next ,
22
- unstable_getCurrentPriorityLevel as getCurrentPriorityLevel ,
23
- unstable_runWithPriority as runWithPriority ,
24
- unstable_ImmediatePriority as ImmediatePriority ,
25
- unstable_UserBlockingPriority as UserBlockingPriority ,
26
- unstable_NormalPriority as NormalPriority ,
27
- unstable_LowPriority as LowPriority ,
28
- unstable_IdlePriority as IdlePriority ,
29
- } from 'scheduler' ;
30
20
import {
31
21
invokeGuardedCallback ,
32
22
hasCaughtError ,
@@ -132,7 +122,7 @@ import {
132
122
computeAsyncExpiration ,
133
123
computeInteractiveExpiration ,
134
124
} from './ReactFiberExpirationTime' ;
135
- import { ConcurrentMode , ProfileMode , NoContext } from './ReactTypeOfMode' ;
125
+ import { ConcurrentMode , ProfileMode } from './ReactTypeOfMode' ;
136
126
import { enqueueUpdate , resetCurrentlyProcessingQueue } from './ReactUpdateQueue' ;
137
127
import { createCapturedValue } from './ReactCapturedValue' ;
138
128
import {
@@ -252,6 +242,11 @@ if (__DEV__) {
252
242
// Used to ensure computeUniqueAsyncExpiration is monotonically decreasing.
253
243
let lastUniqueAsyncExpiration : number = Sync - 1 ;
254
244
245
+ // Represents the expiration time that incoming updates should use. (If this
246
+ // is NoWork, use the default strategy: async updates in async mode, sync
247
+ // updates in sync mode.)
248
+ let expirationContext : ExpirationTime = NoWork ;
249
+
255
250
let isWorking : boolean = false ;
256
251
257
252
// The next work in progress fiber that we're currently working on.
@@ -798,11 +793,9 @@ function commitRoot(root: FiberRoot, finishedWork: Fiber): void {
798
793
// TODO: Avoid this extra callback by mutating the tracing ref directly,
799
794
// like we do at the beginning of commitRoot. I've opted not to do that
800
795
// here because that code is still in flux.
801
- callback = Scheduler_tracing_wrap ( callback ) ;
796
+ callback = Schedule_tracing_wrap ( callback ) ;
802
797
}
803
- passiveEffectCallbackHandle = runWithPriority ( NormalPriority , ( ) => {
804
- return schedulePassiveEffects ( callback ) ;
805
- } ) ;
798
+ passiveEffectCallbackHandle = schedulePassiveEffects ( callback ) ;
806
799
passiveEffectCallback = callback ;
807
800
}
808
801
@@ -1586,58 +1579,52 @@ function computeUniqueAsyncExpiration(): ExpirationTime {
1586
1579
}
1587
1580
1588
1581
function computeExpirationForFiber ( currentTime : ExpirationTime , fiber : Fiber ) {
1589
- const priorityLevel = getCurrentPriorityLevel ( ) ;
1590
-
1591
1582
let expirationTime ;
1592
- if ( ( fiber . mode & ConcurrentMode ) === NoContext ) {
1593
- // Outside of concurrent mode, updates are always synchronous.
1594
- expirationTime = Sync ;
1595
- } else if ( isWorking && ! isCommitting ) {
1596
- // During render phase, updates expire during as the current render.
1597
- expirationTime = nextRenderExpirationTime ;
1583
+ if ( expirationContext !== NoWork ) {
1584
+ // An explicit expiration context was set;
1585
+ expirationTime = expirationContext ;
1586
+ } else if ( isWorking ) {
1587
+ if ( isCommitting ) {
1588
+ // Updates that occur during the commit phase should have sync priority
1589
+ // by default.
1590
+ expirationTime = Sync ;
1591
+ } else {
1592
+ // Updates during the render phase should expire at the same time as
1593
+ // the work that is being rendered.
1594
+ expirationTime = nextRenderExpirationTime ;
1595
+ }
1598
1596
} else {
1599
- switch ( priorityLevel ) {
1600
- case ImmediatePriority :
1601
- expirationTime = Sync ;
1602
- break ;
1603
- case UserBlockingPriority :
1597
+ // No explicit expiration context was set, and we're not currently
1598
+ // performing work. Calculate a new expiration time.
1599
+ if ( fiber . mode & ConcurrentMode ) {
1600
+ if ( isBatchingInteractiveUpdates ) {
1601
+ // This is an interactive update
1604
1602
expirationTime = computeInteractiveExpiration ( currentTime ) ;
1605
- break ;
1606
- case NormalPriority :
1607
- // This is a normal, concurrent update
1603
+ } else {
1604
+ // This is an async update
1608
1605
expirationTime = computeAsyncExpiration ( currentTime ) ;
1609
- break ;
1610
- case LowPriority :
1611
- case IdlePriority :
1612
- expirationTime = Never ;
1613
- break ;
1614
- default :
1615
- invariant (
1616
- false ,
1617
- 'Unknown priority level. This error is likely caused by a bug in ' +
1618
- 'React. Please file an issue.' ,
1619
- ) ;
1620
- }
1621
-
1622
- // If we're in the middle of rendering a tree, do not update at the same
1623
- // expiration time that is already rendering.
1624
- if ( nextRoot !== null && expirationTime === nextRenderExpirationTime ) {
1625
- expirationTime -= 1 ;
1606
+ }
1607
+ // If we're in the middle of rendering a tree, do not update at the same
1608
+ // expiration time that is already rendering.
1609
+ if ( nextRoot !== null && expirationTime === nextRenderExpirationTime ) {
1610
+ expirationTime -= 1 ;
1611
+ }
1612
+ } else {
1613
+ // This is a sync update
1614
+ expirationTime = Sync ;
1626
1615
}
1627
1616
}
1628
-
1629
- // Keep track of the lowest pending interactive expiration time. This
1630
- // allows us to synchronously flush all interactive updates
1631
- // when needed.
1632
- // TODO: Move this to renderer?
1633
- if (
1634
- priorityLevel === UserBlockingPriority &&
1635
- ( lowestPriorityPendingInteractiveExpirationTime === NoWork ||
1636
- expirationTime < lowestPriorityPendingInteractiveExpirationTime )
1637
- ) {
1638
- lowestPriorityPendingInteractiveExpirationTime = expirationTime ;
1617
+ if ( isBatchingInteractiveUpdates ) {
1618
+ // This is an interactive update. Keep track of the lowest pending
1619
+ // interactive expiration time. This allows us to synchronously flush
1620
+ // all interactive updates when needed.
1621
+ if (
1622
+ lowestPriorityPendingInteractiveExpirationTime === NoWork ||
1623
+ expirationTime < lowestPriorityPendingInteractiveExpirationTime
1624
+ ) {
1625
+ lowestPriorityPendingInteractiveExpirationTime = expirationTime ;
1626
+ }
1639
1627
}
1640
-
1641
1628
return expirationTime ;
1642
1629
}
1643
1630
@@ -1875,16 +1862,34 @@ function scheduleWork(fiber: Fiber, expirationTime: ExpirationTime) {
1875
1862
}
1876
1863
}
1877
1864
1865
+ function deferredUpdates < A > (fn: () => A ) : A {
1866
+ const currentTime = requestCurrentTime ( ) ;
1867
+ const previousExpirationContext = expirationContext ;
1868
+ const previousIsBatchingInteractiveUpdates = isBatchingInteractiveUpdates ;
1869
+ expirationContext = computeAsyncExpiration ( currentTime ) ;
1870
+ isBatchingInteractiveUpdates = false ;
1871
+ try {
1872
+ return fn ( ) ;
1873
+ } finally {
1874
+ expirationContext = previousExpirationContext ;
1875
+ isBatchingInteractiveUpdates = previousIsBatchingInteractiveUpdates ;
1876
+ }
1877
+ }
1878
+
1878
1879
function syncUpdates < A , B , C0 , D , R > (
1879
1880
fn : ( A , B , C0 , D ) = > R ,
1880
1881
a : A ,
1881
1882
b : B ,
1882
1883
c : C0 ,
1883
1884
d : D ,
1884
1885
) : R {
1885
- return runWithPriority ( ImmediatePriority , ( ) => {
1886
+ const previousExpirationContext = expirationContext ;
1887
+ expirationContext = Sync ;
1888
+ try {
1886
1889
return fn ( a , b , c , d ) ;
1887
- } ) ;
1890
+ } finally {
1891
+ expirationContext = previousExpirationContext ;
1892
+ }
1888
1893
}
1889
1894
1890
1895
// TODO: Everything below this is written as if it has been lifted to the
@@ -1905,6 +1910,7 @@ let unhandledError: mixed | null = null;
1905
1910
1906
1911
let isBatchingUpdates : boolean = false ;
1907
1912
let isUnbatchingUpdates : boolean = false ;
1913
+ let isBatchingInteractiveUpdates : boolean = false ;
1908
1914
1909
1915
let completedBatches : Array < Batch > | null = null ;
1910
1916
@@ -2435,9 +2441,7 @@ function completeRoot(
2435
2441
lastCommittedRootDuringThisBatch = root ;
2436
2442
nestedUpdateCount = 0 ;
2437
2443
}
2438
- runWithPriority ( ImmediatePriority , ( ) => {
2439
- commitRoot ( root , finishedWork ) ;
2440
- } ) ;
2444
+ commitRoot ( root , finishedWork ) ;
2441
2445
}
2442
2446
2443
2447
function onUncaughtError ( error : mixed ) {
@@ -2503,6 +2507,9 @@ function flushSync<A, R>(fn: (a: A) => R, a: A): R {
2503
2507
}
2504
2508
2505
2509
function interactiveUpdates < A , B , R > ( fn : ( A , B ) = > R , a : A , b : B ) : R {
2510
+ if ( isBatchingInteractiveUpdates ) {
2511
+ return fn ( a , b ) ;
2512
+ }
2506
2513
// If there are any pending interactive updates, synchronously flush them.
2507
2514
// This needs to happen before we read any handlers, because the effect of
2508
2515
// the previous event may influence which handlers are called during
@@ -2516,13 +2523,14 @@ function interactiveUpdates<A, B, R>(fn: (A, B) => R, a: A, b: B): R {
2516
2523
performWork ( lowestPriorityPendingInteractiveExpirationTime , false ) ;
2517
2524
lowestPriorityPendingInteractiveExpirationTime = NoWork ;
2518
2525
}
2526
+ const previousIsBatchingInteractiveUpdates = isBatchingInteractiveUpdates ;
2519
2527
const previousIsBatchingUpdates = isBatchingUpdates ;
2528
+ isBatchingInteractiveUpdates = true ;
2520
2529
isBatchingUpdates = true ;
2521
2530
try {
2522
- return runWithPriority ( UserBlockingPriority , ( ) => {
2523
- return fn ( a , b ) ;
2524
- } ) ;
2531
+ return fn ( a , b ) ;
2525
2532
} finally {
2533
+ isBatchingInteractiveUpdates = previousIsBatchingInteractiveUpdates ;
2526
2534
isBatchingUpdates = previousIsBatchingUpdates ;
2527
2535
if ( ! isBatchingUpdates && ! isRendering ) {
2528
2536
performSyncWork ( ) ;
@@ -2572,7 +2580,7 @@ export {
2572
2580
unbatchedUpdates ,
2573
2581
flushSync ,
2574
2582
flushControlled ,
2575
- Scheduler_next as deferredUpdates ,
2583
+ deferredUpdates ,
2576
2584
syncUpdates ,
2577
2585
interactiveUpdates ,
2578
2586
flushInteractiveUpdates ,
0 commit comments