@@ -15,8 +15,18 @@ import type {Interaction} from 'scheduler/src/Tracing';
15
15
import {
16
16
__interactionsRef ,
17
17
__subscriberRef ,
18
- unstable_wrap as Schedule_tracing_wrap ,
18
+ unstable_wrap as Scheduler_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' ;
20
30
import {
21
31
invokeGuardedCallback ,
22
32
hasCaughtError ,
@@ -122,7 +132,7 @@ import {
122
132
computeAsyncExpiration ,
123
133
computeInteractiveExpiration ,
124
134
} from './ReactFiberExpirationTime' ;
125
- import { ConcurrentMode , ProfileMode } from './ReactTypeOfMode' ;
135
+ import { ConcurrentMode , ProfileMode , NoContext } from './ReactTypeOfMode' ;
126
136
import { enqueueUpdate , resetCurrentlyProcessingQueue } from './ReactUpdateQueue' ;
127
137
import { createCapturedValue } from './ReactCapturedValue' ;
128
138
import {
@@ -242,11 +252,6 @@ if (__DEV__) {
242
252
// Used to ensure computeUniqueAsyncExpiration is monotonically decreasing.
243
253
let lastUniqueAsyncExpiration : number = Sync - 1 ;
244
254
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
-
250
255
let isWorking : boolean = false ;
251
256
252
257
// The next work in progress fiber that we're currently working on.
@@ -793,9 +798,11 @@ function commitRoot(root: FiberRoot, finishedWork: Fiber): void {
793
798
// TODO: Avoid this extra callback by mutating the tracing ref directly,
794
799
// like we do at the beginning of commitRoot. I've opted not to do that
795
800
// here because that code is still in flux.
796
- callback = Schedule_tracing_wrap ( callback ) ;
801
+ callback = Scheduler_tracing_wrap ( callback ) ;
797
802
}
798
- passiveEffectCallbackHandle = schedulePassiveEffects ( callback ) ;
803
+ passiveEffectCallbackHandle = runWithPriority ( NormalPriority , ( ) => {
804
+ return schedulePassiveEffects ( callback ) ;
805
+ } ) ;
799
806
passiveEffectCallback = callback ;
800
807
}
801
808
@@ -1579,52 +1586,58 @@ function computeUniqueAsyncExpiration(): ExpirationTime {
1579
1586
}
1580
1587
1581
1588
function computeExpirationForFiber ( currentTime : ExpirationTime , fiber : Fiber ) {
1589
+ const priorityLevel = getCurrentPriorityLevel ( ) ;
1590
+
1582
1591
let expirationTime ;
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
- }
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 ;
1596
1598
} else {
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
1599
+ switch ( priorityLevel ) {
1600
+ case ImmediatePriority :
1601
+ expirationTime = Sync ;
1602
+ break ;
1603
+ case UserBlockingPriority :
1602
1604
expirationTime = computeInteractiveExpiration ( currentTime ) ;
1603
- } else {
1604
- // This is an async update
1605
+ break ;
1606
+ case NormalPriority :
1607
+ // This is a normal, concurrent update
1605
1608
expirationTime = computeAsyncExpiration ( currentTime ) ;
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 ;
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
+ ) ;
1615
1620
}
1616
- }
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 ;
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 ;
1626
1626
}
1627
1627
}
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 ;
1639
+ }
1640
+
1628
1641
return expirationTime ;
1629
1642
}
1630
1643
@@ -1862,34 +1875,16 @@ function scheduleWork(fiber: Fiber, expirationTime: ExpirationTime) {
1862
1875
}
1863
1876
}
1864
1877
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
-
1879
1878
function syncUpdates < A , B , C0 , D , R > (
1880
1879
fn: (A, B, C0, D) => R ,
1881
1880
a : A ,
1882
1881
b : B ,
1883
1882
c : C0 ,
1884
1883
d : D ,
1885
1884
) : R {
1886
- const previousExpirationContext = expirationContext ;
1887
- expirationContext = Sync ;
1888
- try {
1885
+ return runWithPriority ( ImmediatePriority , ( ) => {
1889
1886
return fn ( a , b , c , d ) ;
1890
- } finally {
1891
- expirationContext = previousExpirationContext ;
1892
- }
1887
+ } ) ;
1893
1888
}
1894
1889
1895
1890
// TODO: Everything below this is written as if it has been lifted to the
@@ -1910,7 +1905,6 @@ let unhandledError: mixed | null = null;
1910
1905
1911
1906
let isBatchingUpdates: boolean = false;
1912
1907
let isUnbatchingUpdates: boolean = false;
1913
- let isBatchingInteractiveUpdates : boolean = false ;
1914
1908
1915
1909
let completedBatches: Array< Batch > | null = null;
1916
1910
@@ -2441,7 +2435,9 @@ function completeRoot(
2441
2435
lastCommittedRootDuringThisBatch = root ;
2442
2436
nestedUpdateCount = 0 ;
2443
2437
}
2444
- commitRoot ( root , finishedWork ) ;
2438
+ runWithPriority ( ImmediatePriority , ( ) => {
2439
+ commitRoot ( root , finishedWork ) ;
2440
+ } ) ;
2445
2441
}
2446
2442
2447
2443
function onUncaughtError ( error : mixed ) {
@@ -2507,9 +2503,6 @@ function flushSync<A, R>(fn: (a: A) => R, a: A): R {
2507
2503
}
2508
2504
2509
2505
function interactiveUpdates < A , B , R > ( fn : ( A , B ) = > R , a : A , b : B ) : R {
2510
- if ( isBatchingInteractiveUpdates ) {
2511
- return fn ( a , b ) ;
2512
- }
2513
2506
// If there are any pending interactive updates, synchronously flush them.
2514
2507
// This needs to happen before we read any handlers, because the effect of
2515
2508
// the previous event may influence which handlers are called during
@@ -2523,14 +2516,13 @@ function interactiveUpdates<A, B, R>(fn: (A, B) => R, a: A, b: B): R {
2523
2516
performWork ( lowestPriorityPendingInteractiveExpirationTime , false ) ;
2524
2517
lowestPriorityPendingInteractiveExpirationTime = NoWork ;
2525
2518
}
2526
- const previousIsBatchingInteractiveUpdates = isBatchingInteractiveUpdates ;
2527
2519
const previousIsBatchingUpdates = isBatchingUpdates ;
2528
- isBatchingInteractiveUpdates = true ;
2529
2520
isBatchingUpdates = true ;
2530
2521
try {
2531
- return fn ( a , b ) ;
2522
+ return runWithPriority ( UserBlockingPriority , ( ) => {
2523
+ return fn ( a , b ) ;
2524
+ } ) ;
2532
2525
} finally {
2533
- isBatchingInteractiveUpdates = previousIsBatchingInteractiveUpdates ;
2534
2526
isBatchingUpdates = previousIsBatchingUpdates ;
2535
2527
if ( ! isBatchingUpdates && ! isRendering ) {
2536
2528
performSyncWork ( ) ;
@@ -2580,7 +2572,7 @@ export {
2580
2572
unbatchedUpdates ,
2581
2573
flushSync ,
2582
2574
flushControlled ,
2583
- deferredUpdates ,
2575
+ Scheduler_next as deferredUpdates ,
2584
2576
syncUpdates ,
2585
2577
interactiveUpdates ,
2586
2578
flushInteractiveUpdates ,
0 commit comments