@@ -291,6 +291,143 @@ 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
+ let [ state , setState ] = React . useState ( router . state ) ;
319
+
320
+ ( router as any ) . __setPendingRerender = ( promise : Promise < ( ) => void > ) =>
321
+ startRevalidation (
322
+ // @ts -expect-error - need react 19 types for this to be async
323
+ async ( ) => {
324
+ const rerender = await promise ;
325
+ startRevalidation ( ( ) => {
326
+ rerender ( ) ;
327
+ } ) ;
328
+ } ,
329
+ ) ;
330
+
331
+ let navigator = React . useMemo ( ( ) : Navigator => {
332
+ return {
333
+ createHref : router . createHref ,
334
+ encodeLocation : router . encodeLocation ,
335
+ go : ( n ) => router . navigate ( n ) ,
336
+ push : ( to , state , opts ) =>
337
+ router . navigate ( to , {
338
+ state,
339
+ preventScrollReset : opts ?. preventScrollReset ,
340
+ } ) ,
341
+ replace : ( to , state , opts ) =>
342
+ router . navigate ( to , {
343
+ replace : true ,
344
+ state,
345
+ preventScrollReset : opts ?. preventScrollReset ,
346
+ } ) ,
347
+ } ;
348
+ } , [ router ] ) ;
349
+
350
+ let basename = router . basename || "/" ;
351
+
352
+ let dataRouterContext = React . useMemo (
353
+ ( ) => ( {
354
+ router,
355
+ navigator,
356
+ static : false ,
357
+ basename,
358
+ unstable_onError,
359
+ } ) ,
360
+ [ router , navigator , basename , unstable_onError ] ,
361
+ ) ;
362
+
363
+ React . useLayoutEffect ( ( ) => {
364
+ return router . subscribe (
365
+ ( newState , { deletedFetchers, flushSync, viewTransitionOpts } ) => {
366
+ newState . fetchers . forEach ( ( fetcher , key ) => {
367
+ if ( fetcher . data !== undefined ) {
368
+ fetcherData . current . set ( key , fetcher . data ) ;
369
+ }
370
+ } ) ;
371
+ deletedFetchers . forEach ( ( key ) => fetcherData . current . delete ( key ) ) ;
372
+
373
+ const diff = shallowDiff ( state , newState ) ;
374
+
375
+ if ( ! diff ) return ;
376
+
377
+ if ( flushSync ) {
378
+ if ( reactDomFlushSyncImpl ) {
379
+ reactDomFlushSyncImpl ( ( ) => setState ( newState ) ) ;
380
+ } else {
381
+ setState ( newState ) ;
382
+ }
383
+ } else {
384
+ React . startTransition ( ( ) => {
385
+ setState ( newState ) ;
386
+ } ) ;
387
+ }
388
+ } ,
389
+ ) ;
390
+ } , [ router , reactDomFlushSyncImpl , state ] ) ;
391
+
392
+ // The fragment and {null} here are important! We need them to keep React 18's
393
+ // useId happy when we are server-rendering since we may have a <script> here
394
+ // containing the hydrated server-side staticContext (from StaticRouterProvider).
395
+ // useId relies on the component tree structure to generate deterministic id's
396
+ // so we need to ensure it remains the same on the client even though
397
+ // we don't need the <script> tag
398
+ return (
399
+ < >
400
+ < DataRouterContext . Provider value = { dataRouterContext } >
401
+ < DataRouterStateContext . Provider
402
+ value = { {
403
+ ...state ,
404
+ revalidation : revalidating ? "loading" : state . revalidation ,
405
+ } }
406
+ >
407
+ < FetchersContext . Provider value = { fetcherData . current } >
408
+ { /* <ViewTransitionContext.Provider value={vtContext}> */ }
409
+ < Router
410
+ basename = { basename }
411
+ location = { state . location }
412
+ navigationType = { state . historyAction }
413
+ navigator = { navigator }
414
+ >
415
+ < MemoizedDataRoutes
416
+ routes = { router . routes }
417
+ future = { router . future }
418
+ state = { state }
419
+ unstable_onError = { unstable_onError }
420
+ />
421
+ </ Router >
422
+ { /* </ViewTransitionContext.Provider> */ }
423
+ </ FetchersContext . Provider >
424
+ </ DataRouterStateContext . Provider >
425
+ </ DataRouterContext . Provider >
426
+ { null }
427
+ </ >
428
+ ) ;
429
+ }
430
+
294
431
/**
295
432
* Render the UI for the given {@link DataRouter}. This component should
296
433
* typically be at the top of an app's element tree.
0 commit comments