@@ -291,6 +291,137 @@ export interface RouterProviderProps {
291291 unstable_onError ?: unstable_ClientOnErrorFunction ;
292292}
293293
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+
294425/**
295426 * Render the UI for the given {@link DataRouter}. This component should
296427 * typically be at the top of an app's element tree.
0 commit comments