@@ -291,6 +291,137 @@ export interface RouterProviderProps {
291
291
unstable_onError ?: unstable_ClientOnErrorFunction ;
292
292
}
293
293
294
+ function shallowDiff ( a : any , b : any ) {
295
+ if ( a === b ) {
296
+ return false ;
297
+ }
298
+ let aKeys = Object . keys ( a ) ;
299
+ let bKeys = Object . keys ( b ) ;
300
+ if ( aKeys . length !== bKeys . length ) {
301
+ return true ;
302
+ }
303
+ for ( let key of aKeys ) {
304
+ if ( a [ key ] !== b [ key ] ) {
305
+ return true ;
306
+ }
307
+ }
308
+ return false ;
309
+ }
310
+
311
+ export function UNSTABLE_TransitionEnabledRouterProvider ( {
312
+ router,
313
+ flushSync : reactDomFlushSyncImpl ,
314
+ unstable_onError,
315
+ } : RouterProviderProps ) {
316
+ let fetcherData = React . useRef < Map < string , any > > ( new Map ( ) ) ;
317
+ let [ revalidating , startRevalidation ] = React . useTransition ( ) ;
318
+ ( router as any ) . __startRevalidation = startRevalidation ;
319
+ let [ _state , setState ] = React . useState ( router . state ) ;
320
+ let [ state , setOptimisticState ] = (
321
+ ( React as any ) . useOptimistic as typeof React . useState
322
+ ) ( _state ) ;
323
+
324
+ let navigator = React . useMemo ( ( ) : Navigator => {
325
+ return {
326
+ createHref : router . createHref ,
327
+ encodeLocation : router . encodeLocation ,
328
+ go : ( n ) => router . navigate ( n ) ,
329
+ push : ( to , state , opts ) =>
330
+ router . navigate ( to , {
331
+ state,
332
+ preventScrollReset : opts ?. preventScrollReset ,
333
+ } ) ,
334
+ replace : ( to , state , opts ) =>
335
+ router . navigate ( to , {
336
+ replace : true ,
337
+ state,
338
+ preventScrollReset : opts ?. preventScrollReset ,
339
+ } ) ,
340
+ } ;
341
+ } , [ router ] ) ;
342
+
343
+ let basename = router . basename || "/" ;
344
+
345
+ let dataRouterContext = React . useMemo (
346
+ ( ) => ( {
347
+ router,
348
+ navigator,
349
+ static : false ,
350
+ basename,
351
+ unstable_onError,
352
+ } ) ,
353
+ [ router , navigator , basename , unstable_onError ] ,
354
+ ) ;
355
+
356
+ React . useLayoutEffect ( ( ) => {
357
+ return router . subscribe (
358
+ ( newState , { deletedFetchers, flushSync, viewTransitionOpts } ) => {
359
+ newState . fetchers . forEach ( ( fetcher , key ) => {
360
+ if ( fetcher . data !== undefined ) {
361
+ fetcherData . current . set ( key , fetcher . data ) ;
362
+ }
363
+ } ) ;
364
+ deletedFetchers . forEach ( ( key ) => fetcherData . current . delete ( key ) ) ;
365
+
366
+ const diff = shallowDiff ( state , newState ) ;
367
+
368
+ if ( ! diff ) return ;
369
+
370
+ if ( flushSync ) {
371
+ if ( reactDomFlushSyncImpl ) {
372
+ reactDomFlushSyncImpl ( ( ) => setState ( newState ) ) ;
373
+ } else {
374
+ setState ( newState ) ;
375
+ }
376
+ } else {
377
+ React . startTransition ( ( ) => {
378
+ setOptimisticState ( newState ) ;
379
+ setState ( newState ) ;
380
+ } ) ;
381
+ }
382
+ } ,
383
+ ) ;
384
+ } , [ router , reactDomFlushSyncImpl , state , setOptimisticState ] ) ;
385
+
386
+ // The fragment and {null} here are important! We need them to keep React 18's
387
+ // useId happy when we are server-rendering since we may have a <script> here
388
+ // containing the hydrated server-side staticContext (from StaticRouterProvider).
389
+ // useId relies on the component tree structure to generate deterministic id's
390
+ // so we need to ensure it remains the same on the client even though
391
+ // we don't need the <script> tag
392
+ return (
393
+ < >
394
+ < DataRouterContext . Provider value = { dataRouterContext } >
395
+ < DataRouterStateContext . Provider
396
+ value = { {
397
+ ...state ,
398
+ revalidation : revalidating ? "loading" : state . revalidation ,
399
+ } }
400
+ >
401
+ < FetchersContext . Provider value = { fetcherData . current } >
402
+ { /* <ViewTransitionContext.Provider value={vtContext}> */ }
403
+ < Router
404
+ basename = { basename }
405
+ location = { state . location }
406
+ navigationType = { state . historyAction }
407
+ navigator = { navigator }
408
+ >
409
+ < MemoizedDataRoutes
410
+ routes = { router . routes }
411
+ future = { router . future }
412
+ state = { state }
413
+ unstable_onError = { unstable_onError }
414
+ />
415
+ </ Router >
416
+ { /* </ViewTransitionContext.Provider> */ }
417
+ </ FetchersContext . Provider >
418
+ </ DataRouterStateContext . Provider >
419
+ </ DataRouterContext . Provider >
420
+ { null }
421
+ </ >
422
+ ) ;
423
+ }
424
+
294
425
/**
295
426
* Render the UI for the given {@link DataRouter}. This component should
296
427
* typically be at the top of an app's element tree.
0 commit comments