@@ -999,56 +999,44 @@ function useMutableSource<Source, Snapshot>(
999999 subscribe,
10001000 } : MutableSourceMemoizedState < Source , Snapshot > ) ;
10011001
1002- // Sync the values needed by our subscribe function after each commit.
1002+ // Sync the values needed by our subscription handler after each commit.
10031003 dispatcher . useEffect ( ( ) => {
1004- const didGetSnapshotChange = ! is ( refs . getSnapshot , getSnapshot ) ;
10051004 refs . getSnapshot = getSnapshot ;
10061005
10071006 // Normally the dispatch function for a state hook never changes,
1008- // but in the case of this hook, it will change if getSnapshot changes .
1009- // In that case, the subscription below will have cloesd over the previous function,
1010- // so we use a ref to ensure that handleChange() always has the latest version.
1007+ // but this hook recreates the queue in certain cases to avoid updates from stale sources .
1008+ // handleChange() below needs to reference the dispatch function without re-subscribing ,
1009+ // so we use a ref to ensure that it always has the latest version.
10111010 refs . setSnapshot = setSnapshot ;
10121011
1013- // This effect runs on mount, even though getSnapshot hasn't changed.
1014- // In that case we can avoid the additional checks for a changed snapshot,
1015- // because the subscription effect below will cover this.
1016- if ( didGetSnapshotChange ) {
1017- // Because getSnapshot is shared with subscriptions via a ref,
1018- // we don't resubscribe when getSnapshot changes.
1019- // This means that we also don't check for any missed mutations
1020- // between the render and the passive commit though.
1021- // So we need to check here, just like when we newly subscribe.
1022- const maybeNewVersion = getVersion ( source . _source ) ;
1023- if ( ! is ( version , maybeNewVersion ) ) {
1024- const maybeNewSnapshot = getSnapshot ( source . _source ) ;
1025- if ( ! is ( snapshot , maybeNewSnapshot ) ) {
1026- setSnapshot ( maybeNewSnapshot ) ;
1027-
1028- const currentTime = requestCurrentTimeForUpdate ( ) ;
1029- const suspenseConfig = requestCurrentSuspenseConfig ( ) ;
1030- const expirationTime = computeExpirationForFiber (
1031- currentTime ,
1032- fiber ,
1033- suspenseConfig ,
1034- ) ;
1035- setPendingExpirationTime ( root , expirationTime ) ;
1036-
1037- // If the source mutated between render and now,
1038- // there may be state updates already scheduled from the old getSnapshot.
1039- // Those updates should not commit without this value.
1040- // There is no mechanism currently to associate these updates though,
1041- // so for now we fall back to synchronously flushing all pending updates.
1042- // TODO: Improve this later.
1043- markRootExpiredAtTime ( root , getLastPendingExpirationTime ( root ) ) ;
1044- }
1012+ // Check for a possible change between when we last rendered now.
1013+ const maybeNewVersion = getVersion ( source . _source ) ;
1014+ if ( ! is ( version , maybeNewVersion ) ) {
1015+ const maybeNewSnapshot = getSnapshot ( source . _source ) ;
1016+ if ( ! is ( snapshot , maybeNewSnapshot ) ) {
1017+ setSnapshot ( maybeNewSnapshot ) ;
1018+
1019+ const currentTime = requestCurrentTimeForUpdate ( ) ;
1020+ const suspenseConfig = requestCurrentSuspenseConfig ( ) ;
1021+ const expirationTime = computeExpirationForFiber (
1022+ currentTime ,
1023+ fiber ,
1024+ suspenseConfig ,
1025+ ) ;
1026+ setPendingExpirationTime ( root , expirationTime ) ;
1027+
1028+ // If the source mutated between render and now,
1029+ // there may be state updates already scheduled from the old getSnapshot.
1030+ // Those updates should not commit without this value.
1031+ // There is no mechanism currently to associate these updates though,
1032+ // so for now we fall back to synchronously flushing all pending updates.
1033+ // TODO: Improve this later.
1034+ markRootExpiredAtTime ( root , getLastPendingExpirationTime ( root ) ) ;
10451035 }
10461036 }
1047- } , [ getSnapshot ] ) ;
1037+ } , [ getSnapshot , source , subscribe ] ) ;
10481038
1049- // If we got a new source or subscribe function,
1050- // we'll need to subscribe in a passive effect,
1051- // and also check for any changes that fire between render and subscribe.
1039+ // If we got a new source or subscribe function, re-subscribe in a passive effect.
10521040 dispatcher . useEffect ( ( ) => {
10531041 const handleChange = ( ) => {
10541042 const latestGetSnapshot = refs . getSnapshot ;
@@ -1089,29 +1077,6 @@ function useMutableSource<Source, Snapshot>(
10891077 }
10901078 }
10911079
1092- // Check for a possible change between when we last rendered and when we just subscribed.
1093- const maybeNewVersion = getVersion ( source . _source ) ;
1094- if ( ! is ( version , maybeNewVersion ) ) {
1095- const maybeNewSnapshot = getSnapshot ( source . _source ) ;
1096- if ( ! is ( snapshot , maybeNewSnapshot ) ) {
1097- setSnapshot ( maybeNewSnapshot ) ;
1098-
1099- const currentTime = requestCurrentTimeForUpdate ( ) ;
1100- const suspenseConfig = requestCurrentSuspenseConfig ( ) ;
1101- const expirationTime = computeExpirationForFiber (
1102- currentTime ,
1103- fiber ,
1104- suspenseConfig ,
1105- ) ;
1106- setPendingExpirationTime ( root , expirationTime ) ;
1107-
1108- // We missed a mutation before committing.
1109- // It's possible that other components using this source also have pending updates scheduled.
1110- // In that case, we should ensure they all commit together.
1111- markRootExpiredAtTime ( root , getLastPendingExpirationTime ( root ) ) ;
1112- }
1113- }
1114-
11151080 return unsubscribe ;
11161081 } , [ source , subscribe ] ) ;
11171082
@@ -1126,31 +1091,27 @@ function useMutableSource<Source, Snapshot>(
11261091 //
11271092 // In both cases, we need to throw away pending udpates (since they are no longer relevant)
11281093 // and treat reading from the source as we do in the mount case.
1129- const didGetSnapshotChange = !is(prevGetSnapshot, getSnapshot);
11301094 if (
1095+ ! is ( prevGetSnapshot , getSnapshot ) ||
11311096 ! is ( prevSource , source ) ||
1132- !is(prevSubscribe, subscribe) ||
1133- didGetSnapshotChange
1097+ ! is ( prevSubscribe , subscribe )
11341098 ) {
1135- if ( didGetSnapshotChange ) {
1136- // Create a new queue and setState method,
1137- // So if there are interleaved updates, they get pushed to the older queue.
1138- // When this becomes current, the previous queue and dispatch method will be discarded,
1139- // including any interleaving updates that occur.
1140- const newQueue = {
1141- pending : null ,
1142- dispatch : null ,
1143- lastRenderedReducer : basicStateReducer ,
1144- lastRenderedState : snapshot ,
1145- } ;
1146- newQueue . dispatch = setSnapshot = ( dispatchAction . bind (
1147- null ,
1148- currentlyRenderingFiber ,
1149- newQueue ,
1150- ) : any ) ;
1151- stateHook . queue = newQueue ;
1152- }
1153-
1099+ // Create a new queue and setState method,
1100+ // So if there are interleaved updates, they get pushed to the older queue.
1101+ // When this becomes current, the previous queue and dispatch method will be discarded,
1102+ // including any interleaving updates that occur.
1103+ const newQueue = {
1104+ pending : null ,
1105+ dispatch : null ,
1106+ lastRenderedReducer : basicStateReducer ,
1107+ lastRenderedState : snapshot ,
1108+ } ;
1109+ newQueue . dispatch = setSnapshot = ( dispatchAction . bind (
1110+ null ,
1111+ currentlyRenderingFiber ,
1112+ newQueue ,
1113+ ) : any ) ;
1114+ stateHook . queue = newQueue ;
11541115 stateHook . baseQueue = null ;
11551116 snapshot = readFromUnsubcribedMutableSource ( root , source , getSnapshot ) ;
11561117 stateHook . memoizedState = stateHook . baseState = snapshot ;
0 commit comments