@@ -51,6 +51,7 @@ import {
5151  markRootMutableRead , 
5252}  from  './ReactFiberLane.new' ; 
5353import  { 
54+   DiscreteEventPriority , 
5455  ContinuousEventPriority , 
5556  getCurrentUpdatePriority , 
5657  setCurrentUpdatePriority , 
@@ -136,6 +137,7 @@ export type UpdateQueue<S, A> = {|
136137
137138let  didWarnAboutMismatchedHooksForComponent ; 
138139let  didWarnAboutUseOpaqueIdentifier ; 
140+ let  didWarnUncachedGetSnapshot ; 
139141if  ( __DEV__ )  { 
140142  didWarnAboutUseOpaqueIdentifier  =  { } ; 
141143  didWarnAboutMismatchedHooksForComponent  =  new  Set ( ) ; 
@@ -1246,14 +1248,127 @@ function mountSyncExternalStore<T>(
12461248  subscribe: (() =>  void )  =>  ( )  =>  void , 
12471249  getSnapshot : ( )  =>  T , 
12481250) : T  { 
1249-   throw  new  Error ( 'Not yet implemented' ) ; 
1251+   const  hook  =  mountWorkInProgressHook ( ) ; 
1252+   return  useSyncExternalStore ( hook ,  subscribe ,  getSnapshot ) ; 
12501253} 
12511254
12521255function updateSyncExternalStore< T > (
12531256  subscribe: (() =>  void )  =>  ( )  =>  void , 
12541257  getSnapshot : ( )  =>  T , 
12551258) : T  { 
1256-   throw  new  Error ( 'Not yet implemented' ) ; 
1259+   const  hook  =  updateWorkInProgressHook ( ) ; 
1260+   return  useSyncExternalStore ( hook ,  subscribe ,  getSnapshot ) ; 
1261+ } 
1262+ 
1263+ function useSyncExternalStore< T > (
1264+   hook: Hook,
1265+   subscribe: (() =>  void )  =>  ( )  =>  void , 
1266+   getSnapshot : ( )  =>  T , 
1267+ ) : T  { 
1268+   // TODO: This is a copy-paste of the userspace shim. We can improve the 
1269+   // built-in implementation using lower-level APIs. We also intend to move 
1270+   // the tearing checks to an earlier, pre-commit phase so that the layout 
1271+   // effects always observe a consistent tree. 
1272+ 
1273+   const  dispatcher  =  ReactCurrentDispatcher . current ; 
1274+ 
1275+   // Read the current snapshot from the store on every render. Again, this 
1276+   // breaks the rules of React, and only works here because of specific 
1277+   // implementation details, most importantly that updates are 
1278+   // always synchronous. 
1279+   const  value  =  getSnapshot ( ) ; 
1280+   if  ( __DEV__ )  { 
1281+     if  ( ! didWarnUncachedGetSnapshot )  { 
1282+       if  ( value  !==  getSnapshot ( ) )  { 
1283+         console . error ( 
1284+           'The result of getSnapshot should be cached to avoid an infinite loop' , 
1285+         ) ; 
1286+         didWarnUncachedGetSnapshot  =  true ; 
1287+       } 
1288+     } 
1289+   } 
1290+ 
1291+   // Because updates are synchronous, we don't queue them. Instead we force a 
1292+   // re-render whenever the subscribed state changes by updating an some 
1293+   // arbitrary useState hook. Then, during render, we call getSnapshot to read 
1294+   // the current value. 
1295+   // 
1296+   // Because we don't actually use the state returned by the useState hook, we 
1297+   // can save a bit of memory by storing other stuff in that slot. 
1298+   // 
1299+   // To implement the early bailout, we need to track some things on a mutable 
1300+   // object. Usually, we would put that in a useRef hook, but we can stash it in 
1301+   // our useState hook instead. 
1302+   // 
1303+   // To force a re-render, we call forceUpdate({inst}). That works because the 
1304+   // new object always fails an equality check. 
1305+   const  [ { inst} ,  forceUpdate ]  =  dispatcher . useState ( { 
1306+     inst : { value,  getSnapshot} , 
1307+   } ) ; 
1308+ 
1309+   // Track the latest getSnapshot function with a ref. This needs to be updated 
1310+   // in the layout phase so we can access it during the tearing check that 
1311+   // happens on subscribe. 
1312+   // TODO: Circumvent SSR warning 
1313+   dispatcher . useLayoutEffect ( ( )  =>  { 
1314+     inst . value  =  value ; 
1315+     inst . getSnapshot  =  getSnapshot ; 
1316+ 
1317+     // Whenever getSnapshot or subscribe changes, we need to check in the 
1318+     // commit phase if there was an interleaved mutation. In concurrent mode 
1319+     // this can happen all the time, but even in synchronous mode, an earlier 
1320+     // effect may have mutated the store. 
1321+     if  ( checkIfSnapshotChanged ( inst ) )  { 
1322+       // Force a re-render. 
1323+       const  prevTransition  =  ReactCurrentBatchConfig . transition ; 
1324+       const  prevPriority  =  getCurrentUpdatePriority ( ) ; 
1325+       ReactCurrentBatchConfig . transition  =  0 ; 
1326+       setCurrentUpdatePriority ( DiscreteEventPriority ) ; 
1327+       forceUpdate ( { inst} ) ; 
1328+       setCurrentUpdatePriority ( prevPriority ) ; 
1329+       ReactCurrentBatchConfig . transition  =  prevTransition ; 
1330+     } 
1331+   } ,  [ subscribe ,  value ,  getSnapshot ] ) ; 
1332+ 
1333+   dispatcher . useEffect ( ( )  =>  { 
1334+     const  handleStoreChange  =  ( )  =>  { 
1335+       // TODO: Because there is no cross-renderer API for batching updates, it's 
1336+       // up to the consumer of this library to wrap their subscription event 
1337+       // with unstable_batchedUpdates. Should we try to detect when this isn't 
1338+       // the case and print a warning in development? 
1339+ 
1340+       // The store changed. Check if the snapshot changed since the last time we 
1341+       // read from the store. 
1342+       if  ( checkIfSnapshotChanged ( inst ) )  { 
1343+         // Force a re-render. 
1344+         const  prevTransition  =  ReactCurrentBatchConfig . transition ; 
1345+         const  prevPriority  =  getCurrentUpdatePriority ( ) ; 
1346+         ReactCurrentBatchConfig . transition  =  0 ; 
1347+         setCurrentUpdatePriority ( DiscreteEventPriority ) ; 
1348+         forceUpdate ( { inst} ) ; 
1349+         setCurrentUpdatePriority ( prevPriority ) ; 
1350+         ReactCurrentBatchConfig . transition  =  prevTransition ; 
1351+       } 
1352+     } ; 
1353+     // Check for changes right before subscribing. Subsequent changes will be 
1354+     // detected in the subscription handler. 
1355+     handleStoreChange ( ) ; 
1356+     // Subscribe to the store and return a clean-up function. 
1357+     return  subscribe ( handleStoreChange ) ; 
1358+   } ,  [ subscribe ] ) ; 
1359+ 
1360+   return  value ; 
1361+ } 
1362+ 
1363+ function checkIfSnapshotChanged(inst) { 
1364+   const  latestGetSnapshot  =  inst . getSnapshot ; 
1365+   const  prevValue  =  inst . value ; 
1366+   try  { 
1367+     const  nextValue =  latestGetSnapshot ( ) ; 
1368+     return  ! is ( prevValue ,  nextValue ) ; 
1369+   }  catch  ( error )  { 
1370+     return true ; 
1371+   } 
12571372} 
12581373
12591374function mountState< S > (
0 commit comments