Reaccionar el suspenso en la práctica

Índice
  1. Un poco de historia
  2. ¿Qué hace exactamente el suspenso?
  3. Ejemplo: navegación
  4. Ayudantes de navegación
  5. Las piezas de una navegación basada en el suspenso
  6. Todo junto
  7. Buscando y actualizando
  8. Una nota rápida sobre la coherencia
  9. Límites de suspenso anidados
  10. Cómo encajan los datos
  11. Espera, ¿qué pasa con la precarga?
  12. Pero en serio, ¿cómo se precarga?
  13. Terminando

Esta publicación trata sobre cómo funciona Suspense, qué hace y cómo puede integrarse en una aplicación web real. Veremos cómo integrar el enrutamiento y la carga de datos con Suspense en React. Para el enrutamiento, usaré JavaScript básico y usaré mi propia biblioteca GraphQL micro-graphql-react para datos.

Si te preguntas sobre React Router, parece genial, pero nunca tuve la oportunidad de usarlo. Mi propio proyecto paralelo tiene una historia de camino bastante simple que siempre lo hice a mano. Además, usar JavaScript básico nos permitirá ver mejor cómo funciona Suspense.

Un poco de historia

Hablemos del suspenso en sí. Kingsley Silas proporciona una descripción general detallada, pero lo primero que hay que tener en cuenta es que todavía es una API experimental. Eso significa, y los documentos de React dicen lo mismo, no depende de él todavía para el trabajo listo para producción. Siempre existe la posibilidad de que cambie entre ahora y cuando esté completamente completo, así que téngalo en cuenta.

Dicho esto, Suspense se trata de mantener una interfaz de usuario consistente frente a dependencias asincrónicas, como componentes React cargados de forma diferida, datos GraphQL, etc. Suspense proporciona API de bajo nivel que le permiten mantener fácilmente su interfaz de usuario mientras su aplicación administra. estas cosas.

¿Pero qué significa “consistente” en este caso? Significa no representar una interfaz de usuario que esté parcialmente completa. Significa que, si hay tres fuentes de datos en la página y una de ellas se ha completado, no queremos representar ese estado actualizado, con una rueda giratoria junto a los otros dos estados ahora obsoletos.

Lo que queremos hacer es indicarle al usuario que se están cargando datos, mientras continuamos mostrando la interfaz de usuario anterior o una interfaz de usuario alternativa que indica que estamos esperando datos; El suspenso apoya a cualquiera de los dos, algo que abordaré.

¿Qué hace exactamente el suspenso?

Todo esto es menos complicado de lo que parece. Tradicionalmente, en React, establecías el estado y tu interfaz de usuario se actualizaba. La vida era sencilla. Pero también condujo al tipo de inconsistencias descritas anteriormente. Lo que Suspense agrega es la capacidad de hacer que un componente notifique a React en el momento del renderizado que está esperando datos asincrónicos; esto se llama suspensión y puede ocurrir en cualquier parte del árbol de un componente, tantas veces como sea necesario, hasta que el árbol esté listo. Cuando un componente se suspende, React se negará a presentar la actualización del estado pendiente hasta que se hayan satisfecho todas las dependencias suspendidas.

¿Qué sucede entonces cuando un componente se suspende? Reactá en el árbol, encontrará el primer Suspensecomponente y generará su respaldo. Proporcionaré muchos ejemplos, pero por ahora, debes saber que puedes proporcionar esto:

Suspense fallback={Loading /}

…y el Loading /componente se procesará si Suspensese suspende algún componente secundario.

Pero, ¿qué pasa si ya tenemos una interfaz de usuario válida y coherente y el usuario carga datos nuevos, lo que provoca la suspensión de un componente? Esto haría que toda la interfaz de usuario existente se cancele y se muestre el respaldo. Eso seguiría siendo consistente, pero no sería una buena experiencia de usuario. Preferiríamos que la interfaz de usuario anterior permanezca en la pantalla mientras se cargan los datos nuevos.

Para respaldar esto, React proporciona una segunda API, useTransition, que efectivamente realiza un cambio de estado en la memoria . En otras palabras, le permite establecer el estado en la memoria mientras mantiene su interfaz de usuario existente en la pantalla; React prácticamente mantendrá una segunda copia de su árbol de componentes renderizado en la memoria y establecerá el estado en ese árbol. Los componentes pueden suspenderse, pero solo en la memoria, por lo que su interfaz de usuario existente seguirá mostrándose en la pantalla. Cuando se complete el cambio de estado y se hayan resuelto todas las suspensiones, el cambio de estado en la memoria se mostrará en la pantalla. Obviamente, desea brindar retroalimentación a su usuario mientras esto sucede, por lo que useTransitionproporciona un pendingvalor booleano, que puede usar para mostrar algún tipo de notificación de “carga” en línea mientras las suspensiones se resuelven en la memoria.

Cuando lo piensas, probablemente no quieras que tu UI existente se muestre indefinidamente mientras la carga está pendiente. Si el usuario intenta hacer algo y transcurre un largo período de tiempo antes de que finalice, probablemente debería considerar que la interfaz de usuario existente está desactualizada y no es válida. En este punto, probablemente querrá suspender su árbol de componentes y Suspensemostrar su respaldo.

Para lograr esto, useTransitionse necesita un timeoutMsvalor. Esto indica la cantidad de tiempo que está dispuesto a dejar que se ejecute el cambio de estado en memoria, antes de suspenderlo.

const Component = props = {  const [startTransition, isPending] = useTransition({ timeoutMs: 3000 });  // .....};

Aquí startTransitionhay una función. Cuando desee ejecutar un cambio de estado “en la memoria”, llame startTransitiony pase una expresión lambda que cambie su estado.

startTransition(() = {  dispatch({ type: LOAD_DATA_OR_SOMETHING, value: 42 });})

Puedes llamar startTransitiona donde quieras. Puede pasarlo a componentes secundarios, etc. Cuando lo llame, cualquier cambio de estado que realice ocurrirá en la memoria. Si ocurre una suspensión, isPendingse hará realidad, lo que puede usar para mostrar algún tipo de indicador de carga en línea.

Eso es todo. Eso es lo que hace Suspenso.

El resto de esta publicación abordará algún código real para aprovechar estas funciones.

Ejemplo: navegación

Para vincular la navegación con Suspense, le alegrará saber que React proporciona una primitiva para hacer esto: React.lazy. Es una función que toma una expresión lambda que devuelve una Promesa, que se resuelve en un componente de React. El resultado de esta llamada a función se convierte en su componente cargado de forma diferente. Suena complicado, pero se ve así:

const SettingsComponent = lazy(() = import("./modules/settings/settings"));

SettingsComponentahora es un componente de React que, cuando se renderice (pero no antes), llamará a la función que le pasó, que llamará import()y cargará el módulo JavaScript ubicado en ./modules/settings/settings.

La pieza clave es esta: mientras import()esté en vuelo, la representación del componente SettingsComponentse suspenderá. Parece que tenemos todas las piezas en la mano, así que juntemoslas y construyamos una navegación basada en suspenso.

Ayudantes de navegación

Pero primero, para contextualizar, cubriré brevemente cómo se administra el estado de navegación en esta aplicación, para que el código Suspense tenga más sentido.

Usaré mi aplicación de lista de libros. Es sólo un proyecto paralelo mío que mantengo principalmente para trastear con tecnología web de última generación. Fue escrito por mí solo, así que espere que algunas partes estén un poco sin refinar (especialmente el diseño).

La aplicación es pequeña, con alrededor de ocho módulos diferentes a los que un usuario puede acceder, sin necesidad de una navegación más profunda. Cualquier estado de búsqueda que pueda utilizar un módulo se almacena en la cadena de consulta de la URL. Teniendo esto en cuenta, existen algunos métodos para eliminar el nombre del módulo actual y el estado de búsqueda de la URL. Este código utiliza los paquetes query-stringy historyde npm y se parece a esto (algunos detalles se han eliminado para simplificar, como la autenticación).

import createHistory from "history/createBrowserHistory";import queryString from "query-string";export const history = createHistory();export function getCurrentUrlState() {  let location = history.location;  let parsed = queryString.parse(location.search);  return {    pathname: location.pathname,    searchState: parsed  };}export function getCurrentModuleFromUrl() {  let location = history.location;  return location.pathname.replace(///g, "").toLowerCase();}

Tengo un appSettingsreductor que contiene el módulo y searchStatelos valores actuales de la aplicación y utiliza estos métodos para sincronizar con la URL cuando sea necesario.

Las piezas de una navegación basada en el suspenso

Comenzamos con un poco de trabajo de suspenso. Primero, creemos los componentes de carga diferentes para nuestros módulos.

const ActivateComponent = lazy(() = import("./modules/activate/activate"));const AuthenticateComponent = lazy(() =  import("./modules/authenticate/authenticate"));const BooksComponent = lazy(() = import("./modules/books/books"));const HomeComponent = lazy(() = import("./modules/home/home"));const ScanComponent = lazy(() = import("./modules/scan/scan"));const SubjectsComponent = lazy(() = import("./modules/subjects/subjects"));const SettingsComponent = lazy(() = import("./modules/settings/settings"));const AdminComponent = lazy(() = import("./modules/admin/admin"));

Ahora necesitamos un método para elegir el componente correcto según el módulo actual. Si estuviéramos usando React Router, tendríamos algunos Route /componentes buenos. Dado que estamos implementando esto manualmente, switchbastará con a.

export const getModuleComponent = moduleToLoad = {  if (moduleToLoad == null) {    return null;  }  switch (moduleToLoad.toLowerCase()) {    case "activate":      return ActivateComponent;    case "authenticate":      return AuthenticateComponent;    case "books":      return BooksComponent;    case "home":      return HomeComponent;    case "scan":      return ScanComponent;    case "subjects":      return SubjectsComponent;    case "settings":      return SettingsComponent;    case "admin":      return AdminComponent;  }    return HomeComponent;};

Todo junto

Una vez terminada toda la aburrida configuración, veamos cómo se ve toda la raíz de la aplicación. Hay mucho código aquí, pero prometo que relativamente pocas de estas líneas pertenecen a Suspense y lo cubriré todo.

const App = () = {  const [startTransitionNewModule, isNewModulePending] = useTransition({    timeoutMs: 3000  });  const [startTransitionModuleUpdate, moduleUpdatePending] = useTransition({    timeoutMs: 3000  });  let appStatePacket = useAppState();  let [appState, _, dispatch] = appStatePacket;  let Component = getModuleComponent(appState.module);  useEffect(() = {    startTransitionNewModule(() = {      dispatch({ type: URL_SYNC });    });  }, []);  useEffect(() = {    return history.listen(location = {      if (appState.module != getCurrentModuleFromUrl()) {        startTransitionNewModule(() = {          dispatch({ type: URL_SYNC });        });      } else {        startTransitionModuleUpdate(() = {          dispatch({ type: URL_SYNC });        });      }    });  }, [appState.module]);  return (    AppContext.Provider value={appStatePacket}      ModuleUpdateContext.Provider value={moduleUpdatePending}        div          MainNavigationBar /          {isNewModulePending ? Loading / : null}          Suspense fallback={LongLoading /}            div style={{ flex: 1, overflowY: "auto" }}              {Component ? Component updating={moduleUpdatePending} / : null}            /div          /Suspense        /div      /ModuleUpdateContext.Provider    /AppContext.Provider  );};

Primero, tenemos dos llamadas diferentes a useTransition. Usaremos uno para enrutar a un nuevo módulo y el otro para actualizar el estado de búsqueda del módulo actual. ¿Por qué la diferencia? Bueno, cuando el estado de búsqueda de un módulo se actualiza, es probable que ese módulo quiera mostrar un indicador de carga en línea. Ese estado de actualización lo mantiene la moduleUpdatePendingvariable, que verás que puse en contexto para que el módulo activo la tome y la use según sea necesario:

div  MainNavigationBar /  {isNewModulePending ? Loading / : null}  Suspense fallback={LongLoading /}    div style={{ flex: 1, overflowY: "auto" }}      {Component ? Component updating={moduleUpdatePending} / : null} // highlight    /div  /Suspense/div

Es appStatePacketel resultado del reductor de estado de la aplicación que mencioné anteriormente (pero que no mostré). Contiene varias partes del estado de la aplicación que rara vez cambian (tema de color, estado fuera de línea, módulo actual, etc.).

let appStatePacket = useAppState();

Un poco más tarde, tomo el componente que esté activo, según el nombre del módulo actual. Inicialmente esto será nulo.

let Component = getModuleComponent(appState.module);

La primera llamada useEffectle indicará a nuestro appSettingsreductor que se sincronice con la URL al inicio.

useEffect(() = {  startTransitionNewModule(() = {    dispatch({ type: URL_SYNC });  });}, []);

Dado que este es el módulo inicial al que navega la aplicación web, lo envuelvo startTransitionNewModulepara indicar que se está cargando un módulo nuevo. Si bien puede resultar tentador que el appSettingsreductor tenga el nombre del módulo inicial como estado inicial, esto nos impide llamar a nuestra startTransitionNewModuledevolución de llamada, lo que significa que nuestro límite de Suspense generaría la reserva inmediatamente, en lugar de después del tiempo de espera.

La próxima llamada para useEffectconfigurar una suscripción al historial. Pase lo que pase, cuando la URL cambia, le indicamos a la configuración de nuestra aplicación que se sincronice con la URL. La única diferencia es en qué startTransitionse incluye esa misma llamada.

useEffect(() = {  return history.listen(location = {    if (appState.module != getCurrentModuleFromUrl()) {      startTransitionNewModule(() = {        dispatch({ type: URL_SYNC });      });    } else {      startTransitionModuleUpdate(() = {        dispatch({ type: URL_SYNC });      });    }  });}, [appState.module]);  

Si estamos buscando un nuevo módulo, llamamos startTransitionNewModule. Si estamos cargando un componente que aún no se ha cargado, React.lazyse suspenderá y se establecerá el indicador pendiente visible solo para la raíz de la aplicación, lo que mostrará una rueda giratoria de carga en la parte superior de la aplicación mientras se recupera el componente diferido y cargado. Debido a su useTransitionfuncionamiento, la pantalla actual seguirá mostrándose durante tres segundos. Si ese tiempo expira y el componente aún no está listo, nuestra interfaz de usuario se suspenderá y se representará el respaldo, lo que mostrará el LongLoading /componente:

{isNewModulePending ? Loading / : null}Suspense fallback={LongLoading /}  div style={{ flex: 1, overflowY: "auto" }}    {Component ? Component updating={moduleUpdatePending} / : null}  /div/Suspense

Si no estamos cambiando módulos, llamamos startTransitionModuleUpdate:

startTransitionModuleUpdate(() = {  dispatch({ type: URL_SYNC });});

Si la actualización provoca una suspensión, se activará el indicador pendiente que estamos poniendo en contexto. El componente activo puede detectar eso y mostrar cualquier indicador de carga en línea que desee. Como antes, si la suspensión dura más de tres segundos, se activará el mismo límite de Suspenso de antes... a menos que, como veremos más adelante, haya un límite de Suspenso más abajo en el árbol.

Una cosa importante a tener en cuenta es que estos tiempos de espera de tres segundos se aplican no solo a la carga del componente, sino también a la preparación para su visualización. Si el componente se carga en dos segundos y, al renderizarse en la memoria (ya que estamos dentro de una startTransitionllamada) se suspende, useTransitioncontinuará esperando hasta un segundo más antes de suspenderse.

Al escribir esta publicación de blog, utilicé los modos de red lentos de Chrome para forzar que la carga fuera lenta y probar mis límites de Suspense. La configuración se encuentra en la pestaña Red de las herramientas de desarrollo de Chrome.

Abramos nuestra aplicación en el módulo de configuración. Esto se llamará:

dispatch({ type: URL_SYNC });

Nuestro appSettingsreductor se sincronizará con la URL y luego configurará el módulo en "configuración". Esto sucederá dentro de startTransitionNewModulemodo que, cuando el componente con carga diferida intente renderizarse, se suspenderá. Como estamos dentro startTransitionNewModule, isNewModulePendingcambiará a truey el Loading /componente se procesará.

Entonces, ¿qué sucede cuando navegamos por un lugar nuevo? Básicamente lo mismo que antes, excepto esta llamada:

dispatch({ type: URL_SYNC });

…vendrá de la segunda instancia de useEffect. Vayamos al módulo de libros y veamos qué sucede. Primero, el control giratorio en línea se muestra como se esperaba:

Buscando y actualizando

Permanezcamos dentro del módulo de libros y actualicemos la cadena de búsqueda de URL para iniciar una nueva búsqueda. Recuerde que antes estábamos detectando el mismo módulo en esa segunda useEffectllamada y usando una useTransitionllamada dedicada para él. A partir de ahí, pusimos el indicador pendiente en contexto para cualquier módulo que estuviera activo para que pudiéramos tomarlo y usarlo.

Veamos algo de código para usarlo realmente. Realmente no hay mucho código relacionado con Suspense aquí. Estoy tomando el valor del contexto y, si es verdadero, renderizo una rueda giratoria en línea encima de mis resultados existentes. Recordemos que esto sucede cuando useTransitionha comenzado una llamada y la aplicación queda suspendida en la memoria . Mientras eso sucede, continuamos mostrando la interfaz de usuario existente, pero con este indicador de carga.

const BookResults: SFC{ books: any; uiView: any } = ({ books, uiView }) = {  const isUpdating = useContext(ModuleUpdateContext);  return (          {!books.length ? (        div          className="alert alert-warning"          style={{ marginTop: "20px", marginRight: "5px" }}                  No books found        /div      ) : null}      {isUpdating ? Loading / : null}      {uiView.isGridView ? (        GridView books={books} /      ) : uiView.isBasicList ? (        BasicListView books={books} /      ) : uiView.isCoversList ? (        CoversView books={books} /      ) : null}    /  );};

Establezcamos un término de búsqueda y veamos qué sucede. Primero, aparece la rueda giratoria en línea.

Luego, si el useTransitiontiempo de espera expira, obtendremos el respaldo del límite de suspenso. El módulo de libros define su propio límite de Suspenso para proporcionar un indicador de carga más preciso, que se ve así:

Este es un punto clave. Al realizar retrocesos en los límites de suspenso, trate de no mostrar ningún tipo de mensaje giratorio o de “carga”. Eso tenía sentido para nuestra navegación de nivel superior porque no hay mucho más que hacer. Pero cuando esté en una parte específica de su aplicación, intente hacer que su respaldo reutilice muchos de los mismos componentes con algún tipo de indicador de carga donde estarían los datos, pero con todo lo demás deshabilitado.

Así es como se ven los componentes relevantes para mi módulo de libros:

const RenderModule: SFC{} = ({}) = {  const uiView = useBookSearchUiView();  const [lastBookResults, setLastBookResults] = useState({    totalPages: 0,    resultsCount: 0  });  return (    div className="standard-module-container margin-bottom-lg"      Suspense fallback={Fallback uiView={uiView} {...lastBookResults} /}        MainContent uiView={uiView} setLastBookResults={setLastBookResults} /      /Suspense    /div  );};const Fallback: SFC{  uiView: BookSearchUiView;  totalPages: number;  resultsCount: number;} = ({ uiView, totalPages, resultsCount }) = {  return (          BooksMenuBarDisabled        totalPages={totalPages}        resultsCount={resultsCount}      /      {uiView.isGridView ? (        GridViewShell /      ) : (        h1          Books are loading i className="fas fa-cog fa-spin"/i        /h1      )}    /  );};

Una nota rápida sobre la coherencia

Antes de continuar, me gustaría señalar una cosa de las capturas de pantalla anteriores. Mire la rueda giratoria en línea que se muestra mientras la búsqueda está pendiente, luego mire la pantalla cuando esa búsqueda se suspendió y, a continuación, los resultados finales:

¿Observa que hay una etiqueta "C++" a la derecha del panel de búsqueda, con una opción para eliminarla de la consulta de búsqueda? O mejor dicho, ¿observas que esa etiqueta solo aparece en las dos segundas capturas de pantalla? En el momento en que se actualiza la URL, se actualiza el estado de la aplicación que rige esa etiqueta; sin embargo, ese estado no se muestra inicialmente. Inicialmente, la actualización del estado se suspende en la memoria (ya que usamos useTransition) y la interfaz de usuario anterior continúa mostrándose.

Luego se presenta el recurso alternativo. El respaldo muestra una versión deshabilitada de esa misma barra de búsqueda, que muestra el estado de búsqueda actual (por elección). Ahora hemos eliminado nuestra interfaz de usuario anterior (ya que ahora es bastante antigua y obsoleta) y estamos esperando la búsqueda que se muestra en la barra de menú deshabilitada.

Este es el tipo de consistencia que te brinda Suspense, de forma gratuita.

Puede dedicar su tiempo a crear buenos estados de aplicación, y React hace el trabajo preliminar de suponer si las cosas están listas, sin que usted tenga que hacer malabarismos con las promesas.

Límites de suspenso anidados

Supongamos que nuestra navegación de nivel superior tarda un poco en cargar nuestro componente de libros hasta el punto de que se muestra nuestro control giratorio "Aún cargando, lo siento" del límite de Suspenso. A partir de ahí, el componente de libros se carga y se representa el nuevo límite de suspenso dentro del componente de libros. Pero luego, a medida que continúa la representación, nuestra consulta de búsqueda de libros se activa y se suspende. ¿Lo que sucederá? ¿Seguirá apareciendo el límite de suspenso de nivel superior hasta que todo esté listo, o tomará el control el límite de suspenso inferior en los libros?

La respuesta es la última. A medida que los nuevos límites de Suspense vayan descendiendo en el árbol, su reserva reemplazará la reserva de cualquier reserva de Suspense anterior que ya estuviera mostrando. Actualmente existe una API inestable para anular esto, pero si estás haciendo un buen trabajo al crear tus alternativas, este es probablemente el comportamiento que deseas. No querrás que "Aún cargando, lo siento" siga apareciendo. Más bien, tan pronto como el componente de libros esté listo, querrás mostrar ese shell con el mensaje de espera más específico.

Ahora, ¿qué pasa si nuestro módulo de libros se carga y comienza a renderizarse mientras el startTransitioncontrol giratorio todavía se muestra y luego se suspende? En otras palabras, imagine que tenemos startTransitionun tiempo de espera de tres segundos, el componente de libros se procesa, el límite de Suspenso anidado está en el árbol de componentes después de un segundo y la consulta de búsqueda se suspende. ¿Pasarán los dos segundos restantes antes de que ese nuevo límite de suspenso anidado genere el respaldo, o el respaldo se mostrará inmediatamente? La respuesta, quizás sorprendente, es que el nuevo respaldo de Suspense se mostrará inmediatamente de forma predeterminada. Esto se debe a que es mejor mostrar una interfaz de usuario nueva y válida lo más rápido posible, para que el usuario pueda ver que las cosas están sucediendo y progresando.

Cómo encajan los datos

La navegación está bien, pero ¿cómo encaja la carga de datos en todo esto?

Encaja de forma completa y transparente. La carga de datos desencadena suspensiones al igual que la navegación con React.lazy, y se engancha en los mismos useTransitionlímites de suspenso. Esto es lo sorprendente de Suspense: todas sus dependencias asíncronas funcionan perfectamente en este mismo sistema. Gestionar estas diversas solicitudes asíncronas manualmente para garantizar la coherencia era una pesadilla antes de Suspense, y es precisamente por eso que nadie lo hacía. Las aplicaciones web eran famosas por sus hilanderos en cascada que se detenían en momentos impredecibles, produciendo interfaces de usuario inconsistentes que solo estaban parcialmente terminadas.

Bien, pero ¿cómo vinculamos realmente la carga de datos con esto? La carga de datos en Suspense es, paradójicamente, más compleja y también más sencilla.

Lo explicaré.

Si está esperando datos, lanzará una promesa en el componente que lee (o intenta leer) los datos. La promesa debe ser coherente según la solicitud de datos. Por lo tanto, cuatro solicitudes repetidas para la misma consulta de búsqueda “C++” deberían arrojar la misma promesa idéntica. Esto implica algún tipo de capa de caché para gestionar todo esto. Probablemente no escribirás esto tú mismo. En lugar de eso, simplemente tendrá esperanza y esperará a que la biblioteca de datos que utiliza se actualice para admitir Suspense.

Esto ya está hecho en mi biblioteca micro-graphql-react. En lugar de usar el useQuerygancho, usará el useSuspenseQuerygancho, que tiene una API idéntica, pero arroja una promesa consistente cuando espera datos.

Espera, ¿qué pasa con la precarga?

¿Se te ha hecho papilla el cerebro al leer otras cosas en Suspense que hablaban sobre cascadas, búsqueda al renderizar, precarga, etc.? No te preocupes por eso. Esto es lo que significa todo.

Digamos que cargas de forma diferida el componente de libros, que procesa y luego solicita algunos datos, lo que provoca un nuevo suspenso. La solicitud de red para el componente y la solicitud de red para los datos se producirán una tras otra, en forma de cascada.

Pero aquí está la parte clave: el estado de la aplicación que condujo a cualquier consulta inicial que se ejecutó cuando se cargó el componente ya estaba disponible cuando comenzó a cargar el componente (que, en este caso, es la URL). Entonces, ¿por qué no “iniciar” la consulta tan pronto como sepa que la necesitará? Tan pronto como navegue hasta /books, ¿por qué no ejecutar la consulta de búsqueda actual en ese mismo momento, de modo que ya esté en funcionamiento cuando se cargue el componente?

De hecho, el módulo micro-graphql-react tiene un preloadmétodo y le insto a que lo utilice. La precarga de datos es una buena optimización del rendimiento, pero no tiene nada que ver con Suspense. Las aplicaciones clásicas de React podrían (y deberían) precargar datos tan pronto como sepan que los necesitarán. Las aplicaciones Vue deben precargar datos tan pronto como sepan que los necesitarán. Las aplicaciones esbeltas deberían... ya entiendes el punto.

La precarga de datos es ortogonal a Suspense, que es algo que puedes hacer literalmente con cualquier marco. También es algo que todos deberíamos haber estado haciendo ya, aunque nadie más lo haya hecho.

Pero en serio, ¿cómo se precarga?

Eso depende de usted. Como mínimo, la lógica para ejecutar la búsqueda actual debe estar completamente separada en su propio módulo independiente. Literalmente, debes asegurarte de que esta función de precarga esté en un archivo por sí sola. No confíe en el paquete web para sacudir los árboles; probablemente enfrentará una tristeza abyecta la próxima vez que audite sus paquetes.

Tienes un preload()método en su propio paquete, así que llámalo. Llámelo cuando sepa que está a punto de navegar a ese módulo. Supongo que React Router tiene algún tipo de API para ejecutar código en un cambio de navegación. Para el código de enrutamiento básico anterior, llamo al método en ese conmutador de enrutamiento de antes. Lo había omitido por brevedad, pero la entrada del libro en realidad se ve así:

switch (moduleToLoad.toLowerCase()) {  case "activate":    return ActivateComponent;  case "authenticate":    return AuthenticateComponent;  case "books":    // preload!!!    booksPreload();    return BooksComponent;

Eso es todo. Aquí hay una demostración en vivo para jugar:

Para modificar el valor del tiempo de espera de suspenso, que por defecto es 3000 ms, navegue hasta Configuración y consulte la pestaña misceláneos. Solo asegúrese de actualizar la página después de modificarla.

Terminando

Pocas veces he estado tan emocionado por algo en el ecosistema de desarrollo web como por Suspense. Es un sistema increíblemente ambicioso para gestionar uno de los problemas más complicados del desarrollo web: la asincronía.

SUSCRÍBETE A NUESTRO BOLETÍN 
No te pierdas de nuestro contenido ni de ninguna de nuestras guías para que puedas avanzar en los juegos que más te gustan.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Subir

Este sitio web utiliza cookies para mejorar tu experiencia mientras navegas por él. Este sitio web utiliza cookies para mejorar tu experiencia de usuario. Al continuar navegando, aceptas su uso. Mas informacion