Suspense
Suspense
es un componente de React que muestra un aviso hasta que sus hijos hayan terminado de cargar.
<Suspense fallback={<Loading />}>
<SomeComponent />
</Suspense>
Uso
Visualización de un aviso mientras algo se está cargando
Puede envolver cualquier parte de su aplicación con un componente Suspense. Si los datos o el código en su hijo aún no se ha cargado, React pasará a renderizar el fallback
prop en su lugar. Por ejemplo:
<>
<Post />
<Suspense fallback={<LoadingSpinner />}>
<Comments />
</Suspense>
</>
Supongamos que Comments
tarda más en cargarse que Post
. Sin un límite de suspensión, React no podría mostrar ninguno de los dos componentes hasta que ambos se hubieran cargado: Post
estaría bloqueado por Comments
.
Debido al límite de Suspense, Post
no necesita esperar a Comments
. React renderiza LoadingSpinner
en su lugar. Una vez que Comments
termina de cargar, React reemplaza LoadingSpinner
con Comments
.
Suspense nunca mostrará ‘agujeros’ involuntarios en tu contenido. Por ejemplo, si PhotoAlbums
se ha cargado pero las Notes
no, con la estructura de abajo, seguirá mostrando un LoadingSpinner
en lugar de toda la Grid
:
<>
<ProfileHeader />
<Suspense fallback={<LoadingSpinner />}>
<Grid>
<PhotoAlbums />
<Notes />
</Grid>
</Suspense>
</>
Para revelar el contenido anidado a medida que se carga, es necesario añadir más límites de suspenso.
Revelar el contenido anidado mientras se carga
Cuando un componente se suspende, se activa el límite sólo del padre más cercano en el árbol. Esto significa que puede anidar varios límites de suspensión para crear una secuencia de carga. Cada límite de suspensión se rellenará a medida que el siguiente nivel de contenido esté disponible.
Para ilustrarlo, considere el siguiente ejemplo:
<Suspense fallback={<BigSpinner />}>
<MainContent>
<Post />
<Suspense fallback={<CommentsGlimmer />}>
<Comments />
</Suspense>
</MainContent>
</Suspense>
La secuencia será:
- Si
Post
aún no se ha cargado,BigSpinner
se muestra en lugar de toda el área de contenido principal. - Una vez que
Post
termina de cargar,BigSpinner
es reemplazado por el contenido principal. - Si aún no se ha cargado
Comments
, se muestraCommentsGlimmer
en su lugar. - Finalmente, una vez que
Comments
termina de cargarse, reemplaza aCommentsGlimmer
.
Componentes Lazy-loading con Suspense
La API lazy
es potenciada por Suspense. Cuando se renderiza un componente importado con lazy
, se suspenderá si no se ha cargado todavía. Esto le permite mostrar un indicador de carga mientras el código de su componente se está cargando.
import { lazy, Suspense, useState } from 'react';
const MarkdownPreview = lazy(() => import('./MarkdownPreview.js'));
function MarkdownEditor() {
const [showPreview, setShowPreview] = useState(false);
// ...
return (
<>
...
{showPreview && (
<Suspense fallback={<Loading />}>
<h2>Preview</h2>
<MarkdownPreview />
</Suspense>
)}
</>
);
}
En este ejemplo, el código de MarkdownPreview
no se cargará hasta que intentes renderizarlo. Si MarkdownPreview
no se ha cargado aún, se mostrará Loading
en su lugar. Prueba a marcar la casilla de verificación:
import { useState, Suspense, lazy } from 'react'; import Loading from './Loading.js'; const MarkdownPreview = lazy(() => delayForDemo(import('./MarkdownPreview.js'))); export default function MarkdownEditor() { const [showPreview, setShowPreview] = useState(false); const [markdown, setMarkdown] = useState('Hello, **world**!'); return ( <> <textarea value={markdown} onChange={e => setMarkdown(e.target.value)} /> <label> <input type="checkbox" checked={showPreview} onChange={e => setShowPreview(e.target.checked)} /> Show preview </label> <hr /> {showPreview && ( <Suspense fallback={<Loading />}> <h2>Preview</h2> <MarkdownPreview markdown={markdown} /> </Suspense> )} </> ); } // Add a fixed delay so you can see the loading state function delayForDemo(promise) { return new Promise(resolve => { setTimeout(resolve, 2000); }).then(() => promise); }
Esta demo se carga con un retraso artificial. La próxima vez que desmarque y marque la casilla de verificación, Preview
se almacenará en la caché, por lo que no se mostrará el estado de carga. Para volver a ver el estado de carga, haz clic en “Reiniciar” en el sandbox.
Referencia
Suspense
Props
children
: La UI actual que se pretende renderizar. Sichildren
se suspende mientras se renderiza, el límite de suspensión pasará a renderizarfallback
.fallback
: Un UI alternativo para renderizar en lugar del UI actual si no ha terminado de cargar. Se acepta cualquier nodo React válido, aunque en la práctica, un fallback es una vista ligera tipo placeholder, como un spinner de carga o un esqueleto. La suspensión cambiará automáticamente afallback
cuandochildren
se suspenda, y volverá achildren
cuando los datos estén listos. Sifallback
se suspende mientras se renderiza, activará el límite de Suspense padre más cercano.
Advertencias
- React no preserva ningún estado para los renders que se suspendieron antes de que pudieran montarse por primera vez. Cuando el componente se haya cargado, React volverá a intentar renderizar el árbol suspendido desde cero.
- Si la suspensión estaba mostrando contenido para el árbol, pero luego se suspendió de nuevo, el
fallback
se mostrará de nuevo a menos que la actualización que lo causó fue causada porstartTransition
ouseDeferredValue
. - Si React necesita ocultar el contenido ya visible porque se suspendió de nuevo, limpiará layout Effects en el árbol de contenido. Cuando el contenido esté listo para ser mostrado de nuevo, React disparará los layout effects de nuevo. Esto le permite asegurarse de que los Effects que miden el diseño del DOM no intentan hacerlo mientras el contenido está oculto.
- React incluye optimizaciones under-the-hood como Streaming Server Rendering y Selective Hydration que se integran con Suspense. Leer una visión general de la arquitectura y reproduce la charla técnica para saber más.
Solución de problemas
¿Cómo puedo evitar que la interfaz de usuario sea sustituida por un fallback durante una actualización?
Reemplazar la interfaz de usuario visible por una de reserva crea una experiencia de usuario discordante. Esto puede ocurrir cuando una actualización hace que un componente se suspenda, y el límite de suspensión más cercano ya está mostrando contenido al usuario.
Para evitar que esto ocurra, marque la actualización como no urgente utilizando startTransition
. Durante una transición, React esperará hasta que se hayan cargado suficientes datos para evitar que aparezca un fallback no deseado:
function handleNextPageClick() {
// If this update suspends, don't hide the already displayed content
startTransition(() => {
setCurrentPage(currentPage + 1);
});
}
Esto evitará ocultar el contenido existente. Sin embargo, cualquier límite de Suspense
recién renderizado seguirá mostrando inmediatamente los fallbacks para evitar el bloqueo de la UI y dejar que el usuario vea el contenido a medida que esté disponible.
React sólo evitará los “fallbacks” no deseados durante las actualizaciones no urgentes. No retrasará una renderización si es el resultado de una actualización urgente. Debe optar por una API como startTransition
o useDeferredValue
.
Si su router está integrado con Suspense, debería envolver sus actualizaciones en startTransition
automáticamente.