Navegación avanzada y carga diferida en React | Nicolás Garzón
Las rutas organizan pantallas, layouts y estados de navegación. No deberían limitarse a cambiar componentes según una cadena.
TypeScript
Copiar < Route path= "dashboard" element= { < DashboardLayout / > } >
< Route index element= { < Overview / > } / >
< Route path= "orders" element= { < Orders / > } / >
< Route path= "orders/:orderId" element= { < OrderDetail / > } / >
< / Route> Outlet representa la región donde aparece la ruta hija.
Filtros, búsqueda, página y selección compartible suelen pertenecer a search params:
TypeScript
Copiar const [ params, setParams] = useSearchParams ( ) ;
const query = params. get ( "query" ) ?? "" ; La URL permite recargar, compartir y usar atrás/adelante.
Lazy loading reduce el bundle inicial. Coloca Suspense y Error Boundaries en límites que mantengan navegación estable.
Una ruta protegida en cliente mejora UX, pero no asegura datos ni operaciones. El servidor debe validar permisos.
Usa enlaces para navegación iniciada por el usuario. Reserva navigate para resultados de acciones o flujos donde no existe un enlace semántico.
Guardar filtros compartibles solo en estado local.
Usar botones con navigate donde corresponde un enlace.
Confundir ocultar rutas con autorizar recursos.
Crear una boundary de carga global para cada navegación.
El router estructura estados navegables; la URL es una fuente de estado y el servidor conserva la autoridad de acceso.
Una navegación real puede necesitar simultáneamente:
Texto
Copiar match de rutas
+ carga de código
+ carga de datos
+ autorización
+ pending
+ error
+ scroll/focus
+ actualización de URLDiseñar únicamente el componente final deja estos estados dispersos.
En Declarative Mode puedes usar lazy y Suspense:
TypeScript
Copiar const ReportsPage = lazy ( ( ) => import ( "./ReportsPage" ) ) ; En Data/Framework Mode, las rutas pueden definir módulos lazy para cargar componente, loader y action bajo el contrato del router. Prefiere la API del modo utilizado para evitar waterfalls donde primero llega el componente y después descubre datos.
Un módulo de ruta puede agrupar:
Componente.
Loader.
Action.
Error boundary.
Metadata.
Headers.
Estrategia de revalidación.
La forma exacta depende del modo y versión. El objetivo conceptual es co-localizar responsabilidades de la URL, no mezclar todo el dominio en un archivo gigante.
TypeScript
Copiar const navigation = useNavigation ( ) ;
const isNavigating = navigation. state === "loading" ;
Navegación a otra URL.
Submit que navega.
Fetcher que muta sin cambiar URL.
Un spinner global puede ser excesivo. Mantén shell, navegación y contenido anterior cuando sea coherente, y muestra pending cerca de la región afectada.
Un fetcher ejecuta loader/action sin navegación:
TypeScript
Copiar const fetcher = useFetcher ( ) ;
< fetcher. Form method= "post" action= { ` /items/ ${ item. id} /favorite ` } >
< button disabled= { fetcher. state !== "idle" } > Favorito< / button>
< / fetcher. Form>
Mutaciones de una fila.
Autocomplete.
Cargar detalles bajo demanda.
Formularios que no cambian de página.
Cada fetcher tiene state propio, lo que evita bloquear toda la ruta.
Después de Actions, React Router puede revalidar datos. No desactives revalidación solo por reducir requests sin analizar consistencia. Una función shouldRevalidate o estrategia avanzada puede optimizar, pero si es incorrecta deja UI stale.
Data routers pueden ejecutar loaders de rutas coincidentes en paralelo. Un waterfall reaparece cuando:
Un loader espera datos que otro podría obtener en paralelo.
El componente hace fetch en effect después de montar.
Un chunk lazy descubre otro chunk.
Los datos dependen de una request previa sin necesidad real.
Dibuja dependencias y adelanta trabajo cuando sea seguro.
Framework Mode puede precargar rutas, código y datos mediante APIs o convenciones. En otros modos puedes iniciar preload por intención. Evalúa:
Hover y focus.
Enlace visible.
Probabilidad de navegación.
Coste de red móvil.
No precargues cada ruta pesada indiscriminadamente.
TypeScript
Copiar setSearchParams ( ( current) => {
const next = new URLSearchParams ( current) ;
next. set ( "query" , query) ;
next. set ( "page" , "1" ) ;
return next;
} ) ; Preserva params no relacionados y define defaults. Decide si cada pulsación debe crear historial o usar replace. Para búsquedas rápidas, debounce puede evitar una entrada por tecla.
Formularios con cambios sin guardar pueden advertir antes de salir. Las APIs de bloqueo deben usarse con cuidado:
No impidas navegación por estados triviales.
Explica qué se perderá.
Considera refresh/cierre del navegador.
Persiste borradores cuando sea posible.
Una confirmación no garantiza que el proceso no se cierre abruptamente.
Puedes leer fetcher.formData para reflejar temporalmente una intención, como favorito. La respuesta y revalidación deben reconciliar el estado. Maneja error y varias operaciones simultáneas por ID.
Una route error boundary puede acceder a errores lanzados por loader/action. Clasifica:
404 esperado.
401/403.
Validación.
Fallo inesperado.
No muestres detalles internos. Permite reintentar o volver a una ruta segura.
En rutas con Actions servidor:
Verifica sesión y permisos.
Valida origen/CSRF según despliegue.
No confíes en method o campos ocultos.
Revisa reverse proxies y host permitido.
Prueba cambios al actualizar versiones del router.
Framework Mode puede combinar SSR, SPA y prerender. Una ruta puede necesitar diferente estrategia según datos. No confundas que React Router sea framework con que toda ruta se renderice siempre en servidor.
Mantén feedback de pending.
Evita reemplazar todo con spinner.
Actualiza heading y title.
Gestiona focus tras errores o cambio completo.
Conserva aria-current en navegación.
Asegura que enlaces sigan siendo enlaces.
Prueba flujos, no solo matches:
Navegación directa y por Link.
Loader lento.
Action exitosa y con error.
Fetcher concurrente.
Revalidación.
Back/forward con search params.
Chunk fallido.
Bloqueo por borrador.
Navegación coordina URL, código, datos y estados visuales.
Fetchers mutan o cargan sin navegar.
Data loaders evitan muchos effects y waterfalls.
Revalidación mantiene consistencia después de Actions.
Prefetch y lazy deben diseñarse juntos.
Seguridad y accesibilidad atraviesan cada ruta.
¿Qué diferencia existe entre navigation y fetcher?
¿Cómo reaparece un waterfall aunque uses loaders?
¿Cuándo usarías replace al cambiar search params?
¿Por qué shouldRevalidate=false puede ser peligroso?
Ver respuestas
Navigation cambia la ubicación; fetcher trabaja sin cambiar URL.
Si haces fetch posterior en effects o encadenas recursos innecesariamente.
Cuando la actualización no merece una entrada de historial, como búsqueda por tecla.
Puede impedir que datos afectados por una mutación se actualicen.
Estado global y stores externos clasifica qué información debe salir del router y qué debe permanecer en URL o caché.