@@ -542,22 +542,31 @@ export function RouterProvider({
542542 nextLocation : Location ;
543543 } > ( ) ;
544544 let fetcherData = React . useRef < Map < string , any > > ( new Map ( ) ) ;
545+ let logErrorsAndSetState = React . useCallback (
546+ ( newState : RouterState ) => {
547+ setStateImpl ( ( prevState ) => {
548+ // Send loader/action errors through handleError
549+ if ( newState . errors && onError ) {
550+ Object . entries ( newState . errors ) . forEach ( ( [ routeId , error ] ) => {
551+ if ( prevState . errors ?. [ routeId ] !== error ) {
552+ onError ( error , {
553+ location : newState . location ,
554+ params : newState . matches [ 0 ] ?. params ?? { } ,
555+ } ) ;
556+ }
557+ } ) ;
558+ }
559+ return newState ;
560+ } ) ;
561+ } ,
562+ [ onError ] ,
563+ ) ;
545564
546565 let setState = React . useCallback < RouterSubscriber > (
547566 (
548567 newState : RouterState ,
549- { deletedFetchers, newErrors , flushSync, viewTransitionOpts } ,
568+ { deletedFetchers, flushSync, viewTransitionOpts } ,
550569 ) => {
551- // Send router errors through onError
552- if ( newErrors && onError ) {
553- Object . values ( newErrors ) . forEach ( ( error ) =>
554- onError ( error , {
555- location : newState . location ,
556- params : newState . matches [ 0 ] ?. params ?? { } ,
557- } ) ,
558- ) ;
559- }
560-
561570 newState . fetchers . forEach ( ( fetcher , key ) => {
562571 if ( fetcher . data !== undefined ) {
563572 fetcherData . current . set ( key , fetcher . data ) ;
@@ -591,9 +600,9 @@ export function RouterProvider({
591600 // just update and be done with it
592601 if ( ! viewTransitionOpts || ! isViewTransitionAvailable ) {
593602 if ( reactDomFlushSyncImpl && flushSync ) {
594- reactDomFlushSyncImpl ( ( ) => setStateImpl ( newState ) ) ;
603+ reactDomFlushSyncImpl ( ( ) => logErrorsAndSetState ( newState ) ) ;
595604 } else {
596- React . startTransition ( ( ) => setStateImpl ( newState ) ) ;
605+ React . startTransition ( ( ) => logErrorsAndSetState ( newState ) ) ;
597606 }
598607 return ;
599608 }
@@ -617,7 +626,7 @@ export function RouterProvider({
617626
618627 // Update the DOM
619628 let t = router . window ! . document . startViewTransition ( ( ) => {
620- reactDomFlushSyncImpl ( ( ) => setStateImpl ( newState ) ) ;
629+ reactDomFlushSyncImpl ( ( ) => logErrorsAndSetState ( newState ) ) ;
621630 } ) ;
622631
623632 // Clean up after the animation completes
@@ -656,7 +665,13 @@ export function RouterProvider({
656665 } ) ;
657666 }
658667 } ,
659- [ router . window , reactDomFlushSyncImpl , transition , renderDfd , onError ] ,
668+ [
669+ router . window ,
670+ reactDomFlushSyncImpl ,
671+ transition ,
672+ renderDfd ,
673+ logErrorsAndSetState ,
674+ ] ,
660675 ) ;
661676
662677 // Need to use a layout effect here so we are subscribed early enough to
@@ -679,7 +694,7 @@ export function RouterProvider({
679694 let newState = pendingState ;
680695 let renderPromise = renderDfd . promise ;
681696 let transition = router . window . document . startViewTransition ( async ( ) => {
682- React . startTransition ( ( ) => setStateImpl ( newState ) ) ;
697+ React . startTransition ( ( ) => logErrorsAndSetState ( newState ) ) ;
683698 await renderPromise ;
684699 } ) ;
685700 transition . finished . finally ( ( ) => {
@@ -690,7 +705,7 @@ export function RouterProvider({
690705 } ) ;
691706 setTransition ( transition ) ;
692707 }
693- } , [ pendingState , renderDfd , router . window ] ) ;
708+ } , [ pendingState , renderDfd , router . window , logErrorsAndSetState ] ) ;
694709
695710 // When the new location finally renders and is committed to the DOM, this
696711 // effect will run to resolve the transition
0 commit comments