@@ -23,6 +23,7 @@ import type {
23
23
} from 'shared/ReactMutableSource' ;
24
24
25
25
import ReactSharedInternals from 'shared/ReactSharedInternals' ;
26
+ import { HostRoot } from 'shared/ReactWorkTags' ;
26
27
27
28
import { NoWork , Sync } from './ReactFiberExpirationTime' ;
28
29
import { readContext } from './ReactFiberNewContext' ;
@@ -45,7 +46,8 @@ import {
45
46
warnIfNotScopedWithMatchingAct ,
46
47
markRenderEventTimeAndConfig ,
47
48
markUnprocessedUpdateTime ,
48
- getMutableSourceMetadata ,
49
+ getMutableSourcePendingExpirationTime ,
50
+ warnAboutUpdateOnUnmountedFiberInDEV ,
49
51
} from './ReactFiberWorkLoop' ;
50
52
51
53
import invariant from 'shared/invariant' ;
@@ -897,13 +899,15 @@ function useMutableSourceImpl<S>(
897
899
}
898
900
}
899
901
900
- const metadata = getMutableSourceMetadata ( source ) ;
902
+ const pendingExpirationTime = getMutableSourcePendingExpirationTime ( source ) ;
901
903
902
- // Is it safe to read from this source during the current render?
903
- // If the source has not yet been subscribed to, we can use the version number to determine this.
904
- // Else we can use the expiration time as an indicator of any future scheduled updates.
905
904
let isSafeToReadFromSource = false ;
906
- if ( metadata . subscriptionCount === 0 ) {
905
+
906
+ // Is it safe to read from this source during the current render?
907
+ // If the source has pending updates, we can use the current render's expiration
908
+ // time to determine if it's safe to read again from the source.
909
+ // If there are no pending updates, we can use the work-in-progress version.
910
+ if ( pendingExpirationTime === null ) {
907
911
const lastReadVersion = getWorkInProgressVersion ( source ) ;
908
912
if ( lastReadVersion === null ) {
909
913
// This is the only case where we need to actually update the version number.
@@ -926,8 +930,8 @@ function useMutableSourceImpl<S>(
926
930
) ;
927
931
928
932
isSafeToReadFromSource =
929
- metadata . expirationTime === NoWork ||
930
- metadata . expirationTime >= expirationTime ;
933
+ pendingExpirationTime === NoWork ||
934
+ pendingExpirationTime >= expirationTime ;
931
935
}
932
936
933
937
let prevMemoizedState = ((hook.memoizedState: any): ?MutableSourceState< S > );
@@ -977,22 +981,43 @@ function useMutableSourceImpl<S>(
977
981
978
982
const create = ( ) => {
979
983
const scheduleUpdate = ( ) => {
980
- const currentTime = requestCurrentTimeForUpdate ( ) ;
981
- const suspenseConfig = requestCurrentSuspenseConfig ( ) ;
982
- const expirationTime = computeExpirationForFiber (
983
- currentTime ,
984
- fiber ,
985
- suspenseConfig ,
986
- ) ;
984
+ let node = fiber ;
985
+ let root = null ;
986
+ while ( node !== null ) {
987
+ if ( node . tag === HostRoot ) {
988
+ root = node . stateNode ;
989
+ break ;
990
+ }
991
+ node = node . return ;
992
+ }
987
993
988
- // Make sure reads during future renders will know there's a pending update.
989
- // This will prevent a higher priority update from reading a newer version of the source,
990
- // and causing a tear between that render and previous renders.
991
- if ( expirationTime > metadata . expirationTime ) {
992
- metadata . expirationTime = expirationTime ;
994
+ if ( root === null ) {
995
+ warnAboutUpdateOnUnmountedFiberInDEV ( fiber ) ;
996
+ return ;
993
997
}
994
998
995
- scheduleWork ( fiber , expirationTime ) ;
999
+ const alreadyScheduledExpirationTime = root . mutableSourcePendingUpdateMap . get (
1000
+ source ,
1001
+ ) ;
1002
+
1003
+ // If an update is already scheduled for this source, re-use the same priority.
1004
+ if ( alreadyScheduledExpirationTime !== undefined ) {
1005
+ scheduleWork ( fiber , alreadyScheduledExpirationTime ) ;
1006
+ } else {
1007
+ const currentTime = requestCurrentTimeForUpdate ( ) ;
1008
+ const suspenseConfig = requestCurrentSuspenseConfig ( ) ;
1009
+ const expirationTime = computeExpirationForFiber (
1010
+ currentTime ,
1011
+ fiber ,
1012
+ suspenseConfig ,
1013
+ ) ;
1014
+ scheduleWork ( fiber , expirationTime ) ;
1015
+
1016
+ // Make sure reads during future renders will know there's a pending update.
1017
+ // This will prevent a higher priority update from reading a newer version of the source,
1018
+ // and causing a tear between that render and previous renders.
1019
+ root . mutableSourcePendingUpdateMap . set ( source , expirationTime ) ;
1020
+ }
996
1021
} ;
997
1022
998
1023
// Was the source mutated between when we rendered and when we're subscribing?
@@ -1002,16 +1027,8 @@ function useMutableSourceImpl<S>(
1002
1027
scheduleUpdate ( ) ;
1003
1028
}
1004
1029
1005
- const unsubscribe = subscribe ( scheduleUpdate ) ;
1006
- metadata . subscriptionCount ++ ;
1007
-
1008
- memoizedState . destroy = ( ) => {
1009
- metadata . subscriptionCount -- ;
1010
-
1011
- // TODO (useMutableSource) If count is 0, flag this source for possible cleanup.
1012
-
1013
- unsubscribe ( ) ;
1014
- } ;
1030
+ // Unsubscribe on destroy.
1031
+ memoizedState . destroy = subscribe ( scheduleUpdate ) ;
1015
1032
1016
1033
return memoizedState . destroy ;
1017
1034
} ;
0 commit comments