Nos permite crear interfaces de usuario más fluidas y responsivas al manejar de manera elegante los componentes que dependen de recursos asíncronos.
¿Qué es React Suspense?
Un componente que nos permite "suspender" el renderizado de un componente hasta que se cumpla cierta condición: típicamente la carga de datos o código. Durante este tiempo de espera, podemos mostrar un fallback (como un spinner o skeleton) que mejora significativamente la UX.
import React, { Suspense } from 'react';
function App() {
return (
<Suspense fallback={<div>Cargando...</div>}>
<ComponentePesado />
</Suspense>
);
}
Casos de Uso
1. Lazy Loading de Componentes
Uno de los usos más comunes de Suspense es con React.lazy() para cargar componentes de forma perezosa:
import React, { Suspense, lazy } from 'react';
const ComponenteLazy = lazy(() => import('./ComponenteLazy'));
function App() {
return (
<div>
<h1>Mi Aplicación</h1>
<Suspense fallback={<div>Cargando componente...</div>}>
<ComponenteLazy />
</Suspense>
</div>
);
}
2. Carga de Datos con Libraries Compatibles
Aunque React Suspense para datos aún está en desarrollo, libraries como SWR y React Query ya ofrecen soporte:
import { Suspense } from 'react';
import useSWR from 'swr';
function Profile({ userId }) {
const { data } = useSWR(`/api/user/${userId}`, fetcher, {
suspense: true
});
return <div>Hola {data.name}</div>;
}
function App() {
return (
<Suspense fallback={<div>Cargando perfil...</div>}>
<Profile userId={123} />
</Suspense>
);
}
Ventajas
Experiencia de Usuario
- Transiciones fluidas: Elimina los parpadeos y estados de carga abruptos
- Feedback visual: Proporciona información clara sobre el estado de la aplicación
- Carga progresiva: Permite mostrar contenido tan pronto como esté disponible
Código más Limpio
- Separación de responsabilidades: El manejo del estado de carga se centraliza
- Menos boilerplate: Reduce la cantidad de código repetitivo
- Mejor legibilidad: El código se enfoca en la lógica de negocio
Implementación Práctica
Ejemplo con Router y Lazy Loading
import React, { Suspense, lazy } from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
// Componentes lazy
const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));
const Contact = lazy(() => import('./pages/Contact'));
// Componente de carga personalizado
function LoadingSpinner() {
return (
<div className="loading-container">
<div className="spinner"></div>
<p>Cargando página...</p>
</div>
);
}
function App() {
return (
<Router>
<nav>
<a href="/">Inicio</a>
<a href="/about">Acerca</a>
<a href="/contact">Contacto</a>
</nav>
<main>
<Suspense fallback={<LoadingSpinner />}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/contact" element={<Contact />} />
</Routes>
</Suspense>
</main>
</Router>
);
}
Suspense Anidado
Control más granular:
function Dashboard() {
return (
<div>
<h1>Dashboard</h1>
<Suspense fallback={<div>Cargando sidebar...</div>}>
<Sidebar />
</Suspense>
<main>
<Suspense fallback={<div>Cargando contenido principal...</div>}>
<MainContent />
<Suspense fallback={<div>Cargando gráficos...</div>}>
<Charts />
</Suspense>
</Suspense>
</main>
</div>
);
}
Mejores Prácticas
1. Fallbacks Apropiados
- Usa skeletons en lugar de spinners cuando sea posible
- Mantén los fallbacks consistentes con el diseño
- Considera el contexto del contenido que se está cargando
function ProductSkeleton() {
return (
<div className="product-skeleton">
<div className="skeleton-image"></div>
<div className="skeleton-title"></div>
<div className="skeleton-price"></div>
</div>
);
}
2. Manejo de Errores
Combina Suspense con Error Boundaries para un manejo robusto:
function ErrorFallback({ error }) {
return (
<div role="alert">
<h2>Algo salió mal:</h2>
<pre>{error.message}</pre>
</div>
);
}
function App() {
return (
<ErrorBoundary fallback={ErrorFallback}>
<Suspense fallback={<LoadingSpinner />}>
<ComponentePrincipal />
</Suspense>
</ErrorBoundary>
);
}
3. Optimización de Bundle Splitting
// Agrupa componentes relacionados
const AdminRoutes = lazy(() =>
import('./routes/AdminRoutes').then(module => ({
default: module.AdminRoutes
}))
);
// Precarga componentes importantes
const Home = lazy(() => import('./pages/Home'));
// Precarga en hover
function NavLink({ to, children }) {
const handleMouseEnter = () => {
import('./pages/About'); // Precarga el componente
};
return (
<Link to={to} onMouseEnter={handleMouseEnter}>
{children}
</Link>
);
}
El Futuro de Suspense
React continúa desarrollando Suspense, especialmente para:
- Server-Side Rendering: Streaming HTML con componentes suspendidos
- Concurrent Features: Integración con las características concurrentes de React
- Data Fetching: Soporte nativo para carga de datos sin libraries externas