Gestión de estados en Next.js

 

 

 


Índice
  1. Reaccionar API principales para datos
  2. Propagación del estado local a través de la aplicación
  3. Crear una jerarquía entre rutas
  4. Los problemas crecientes vuelven a atacar: el infierno de los proveedores
  5. Estado de búsqueda
  6. Consideraciones finales
  7. Inténtalo tú mismo
  8. Referencias

Al combinar algunas API de React, podemos gestionar con precisión estados "simples". Sin embargo, con Next.js podemos encontrar rápidamente situaciones en las que necesitamos adaptarnos a muchos otros requisitos. Echemos un vistazo a algunos patrones para lograr todo eso.

 

Este artículo ha sido apoyado amablemente por nuestros queridos amigos de Netlify , quienes son un grupo diverso de talentos increíbles de todo el mundo y ofrecen una plataforma para desarrolladores web que multiplica la productividad. ¡Gracias!

Este artículo está destinado a utilizarse como introducción a la gestión de estados complejos en una aplicación Next.js. Desafortunadamente, el marco es demasiado versátil para que podamos cubrir todos los casos de uso posibles en este artículo. Pero estas estrategias deberían adaptarse a la gran mayoría de las aplicaciones con pocos o ningún ajuste. Si cree que hay un patrón relevante a considerar, ¡espero verlo en la sección de comentarios!

 

Reaccionar API principales para datos

Solo hay una forma en que una aplicación React transporta datos: pasándolos de los componentes principales a los componentes secundarios. Independientemente de cómo una aplicación administre sus datos, debe pasarlos de arriba a abajo.

A medida que una aplicación crece en complejidad y ramificaciones de su árbol de renderizado, surgen múltiples capas. A veces es necesario transmitir datos a varias capas de componentes principales hasta que finalmente lleguen al componente al que están destinados los datos; esto se denomina Prop Drilling .

Como se podría anticipar: Prop Drilling puede convertirse en un patrón engorroso y propenso a errores a medida que las aplicaciones crecen. Para evitar este problema, viene la API de contexto. La API Context agrega 3 elementos a esta ecuación:

  1. Contexto
    Los datos que se transfieren del Proveedor al Consumidor.
  2. Proveedor de contexto
    El componente del que se originan los datos.
  3. Consumidor de contexto
    El componente que utilizará los datos recibidos.

El proveedor es invariablemente un antecesor del componente consumidor, pero probablemente no sea un antecesor directo. Luego, la API omite todos los demás eslabones de la cadena y entrega los datos (contexto) directamente al consumidor. Esta es la totalidad de la API de contexto, pasando datos. Tiene tanto que ver con los datos como la oficina postal con su correo.

En una aplicación Vanilla React, los datos pueden ser administrados por otras 2 API: useStatey useReducer. Estaría más allá del alcance de este artículo sugerir cuándo usar uno u otro, así que hagámoslo simple diciendo:

  • useState
    Estructura de datos simple y condiciones simples.
  • useReducer
    Estructuras de datos complejas y/o condiciones entrelazadas.

El hecho de que Prop Drilling y Data Management en React se confundan erróneamente ya que un patrón se debe en parte a una falla inherente en la API de contenido heredado. Cuando el renderizado de un componente era bloqueado, shouldComponentUpdateimpedía que el contexto continuara hasta su objetivo. Este problema llevó a los desarrolladores a recurrir a bibliotecas de terceros cuando todo lo que necesitaban era evitar la perforación de puntales.

Para comprobar una comparación de las bibliotecas más útiles, puedo recomendarte esta publicación sobre React State Management .

Next.js es un marco de React. Por lo tanto, cualquiera de las soluciones descritas para las aplicaciones React se puede aplicar a una aplicación Next.js. Algunos requerirán una mayor flexibilidad para configurarlos, otros tendrán las compensaciones redistribuidas en función de las propias funcionalidades de Next.js. Pero todo es 100% utilizable, puedes elegir tu veneno libremente.

Para la mayoría de los casos de uso comunes, la combinación de Context y State/Reducer es suficiente. Consideraremos esto en este artículo y no profundizaremos demasiado en las complejidades de los estados complejos. Sin embargo, tendremos en cuenta que la mayoría de las aplicaciones Jamstack dependen de datos externos, y eso también es cierto.

 

Propagación del estado local a través de la aplicación

Una aplicación Next.js tiene 2 componentes cruciales para manejar todas las páginas y vistas de nuestra aplicación:

  • _document.{t,j}sx
    Este componente se utiliza para definir el marcado estático. Este archivo se representa en el servidor y no se vuelve a representar en el cliente. Úselo para afectar las htmletiquetas bodyy otros metadatos. Si no desea personalizar estas cosas, es opcional incluirlas en su aplicación.
  • _app.{t,j}sx
    Éste se utiliza para definir la lógica que debe extenderse por toda la aplicación. Todo lo que debería estar presente en cada vista de la aplicación pertenece aquí. Úselo para Providermensajes de correo electrónico, definiciones globales, configuraciones de aplicaciones, etc.

Para ser más explícito, aquí se aplican los proveedores de contexto, por ejemplo:

// _app.jsx or _app.tsximport { AppStateProvider } from './my-context'export default function MyApp({ Component, pageProps }) { return ( AppStateProvider Component {...pageProps} / /AppStateProvider )}

Cada vez que se visita una nueva ruta, nuestras páginas pueden aprovechar AppStateContexty transmitir sus definiciones como props. Cuando nuestra aplicación es lo suficientemente simple, solo necesita una definición distribuida de esta manera, el patrón anterior debería ser suficiente. Por ejemplo:

export default function ConsumerPage() { const { state } = useAppStatecontext() return ( p {state} is here! /p )}

Puede comprobar una implementación real de este patrón ContextAPI en nuestro repositorio de demostración .

Si tiene varios estados definidos en un solo contexto, puede comenzar a tener problemas de rendimiento. La razón de esto es que cuando React ve una actualización de estado, realiza todas las renderizaciones necesarias en el DOM. Si ese estado se comparte entre muchos componentes (como ocurre cuando se usa la API de Contexto), podría provocar renderizaciones innecesarias , lo cual no queremos. ¡Sea exigente con las variables de estado que comparte entre los componentes!

Algo que puede hacer para mantenerse organizado al compartir su estado es crear múltiples partes de Contexto (y, por lo tanto, diferentes Proveedores de Contexto) para contener diferentes partes de estado. Por ejemplo, puede compartir la autenticación en un contexto, las preferencias de internacionalización en otro y el tema del sitio web en otro.

Next.js también proporciona un Layoutpatrón que puedes usar para algo como esto, para abstraer toda esta lógica del _apparchivo, manteniéndolo limpio y legible.

// _app.jsx or _app.tsximport { DefaultLayout } from './layout'export default function MyApp({ Component, pageProps }) { const getLayout = Component.getLayout || ( page = DefaultLayout{page}/DefaultLayout ) return getLayout(Component {...pageProps} /)}// layout.jsximport { AppState_1_Provider } from '../context/context-1'import { AppState_2_Provider } from '../context/context-2'export const DefaultLayout = ({ children }) = { return ( AppState_1_Provider AppState_2_Provider div className="container" {children} /div /AppState_2_Provider /AppState_1_Provider )}

Con este patrón, puede crear múltiples proveedores de contexto y mantenerlos bien definidos en un componente de diseño para toda la aplicación. Además, la getLayoutfunción le permitirá anular las definiciones de diseño predeterminadas por página, de modo que cada página pueda tener su propio toque único en lo que se proporciona.

 

Crear una jerarquía entre rutas

Sin embargo, a veces el patrón de diseño puede no ser suficiente. A medida que las aplicaciones se vuelven más complejas, puede surgir la necesidad de establecer una relación de proveedor/consumidor entre rutas. Una ruta envolverá otras rutas y así les proporcionará definiciones comunes en lugar de hacer que los desarrolladores dupliquen el código. Con esto en mente, hay una propuesta contenedora en las discusiones de Next.js para brindar una experiencia fluida al desarrollador para lograrlo.

Por el momento, no existe una solución de configuración baja para este patrón en Next.js, pero a partir de los ejemplos anteriores, podemos encontrar una solución. Tome este fragmento directamente de los documentos :

import Layout from '../components/layout'import NestedLayout from '../components/nested-layout'export default function Page() { return { /** Your content */ }}Page.getLayout = (page) = ( Layout NestedLayout{page}/NestedLayout /Layout)

¡Otra vez el getLayoutpatrón! Ahora se proporciona como propiedad del Pageobjeto. Toma un pageparámetro tal como un componente de React toma el childrenaccesorio, y podemos envolver tantas capas como queramos. Resuma esto en un módulo separado y comparta esta lógica con ciertas rutas: Listas y rankings

// routes/user-management.jsxexport const MainUserManagement = (page) = ( UserInfoProvider UserNavigationLayout {page} /UserNavigationlayout /UserInfoProvider)// user-dashboard.jsximport { MainUserManagement } from '../routes/user-management'export const UserDashboard = (props) = (/)UserDashboard.getLayout = MainUserManagement

Los problemas crecientes vuelven a atacar: el infierno de los proveedores

Gracias a la API Context de React eludimos Prop Drilling , que era el problema que nos propusimos resolver. Ahora tenemos un código legible y podemos pasarlo propsa nuestros componentes tocando solo las capas requeridas.

Con el tiempo, nuestra aplicación crece y el número de propsaplicaciones que deben transmitirse aumenta a un ritmo cada vez más rápido. Si tenemos el cuidado suficiente para aislar y eliminar los renderizados innecesarios, es probable que juntemos una cantidad incontable de elementos Providersen la raíz de nuestros diseños.

export const DefaultLayout = ({ children }) = { return ( AuthProvider UserProvider ThemeProvider SpecialProvider JustAnotherProvider VerySpecificProvider {children} /VerySpecificProvider /JustAnotherProvider /SpecialProvider /ThemeProvider /UserProvider /AuthProvider )}

Esto es lo que llamamos el infierno de proveedores . Y puede empeorar: ¿qué pasa si SpecialProvidersolo está dirigido a un caso de uso específico? ¿Lo agregas en tiempo de ejecución? Agregar tanto el Proveedor como el Consumidor durante el tiempo de ejecución no es exactamente sencillo.

 

Con este terrible tema en mente, Jōtai ha surgido. Es una biblioteca de gestión estatal con una firma muy similar a useState. Bajo el capó, Jōtai también usa la API Context, pero abstrae Provider Hell de nuestro código e incluso ofrece un modo "sin proveedor" en caso de que la aplicación solo requiera una tienda.

Gracias al enfoque ascendente, podemos definir los átomos de Jōtai (la capa de datos de cada componente que se conecta a la tienda) a nivel de componente y la biblioteca se encargará de vincularlos al proveedor. La Providerutilidad en Jōtai incluye algunas funcionalidades adicionales además de las predeterminadas Context.Providerde React. Siempre aislará los valores de cada átomo, pero necesitará una initialValuespropiedad para declarar una matriz de valores predeterminados. Entonces, el ejemplo anterior de Provider Hell se vería así:

import { Provider } from 'jotai'import { AuthAtom, UserAtom, ThemeAtom, SpecialAtom, JustAnotherAtom, VerySpecificAtom} from '@atoms' const DEFAULT_VALUES = [ [AuthAtom, 'value1'], [UserAtom, 'value2'], [ThemeAtom, 'value3'], [SpecialAtom, 'value4'], [JustAnotherAtom, 'value5'], [VerySpecificAtom, 'value6']]export const DefaultLayout = ({ children }) = { return (  {children}  )}

Jōtai también ofrece otros enfoques para componer y derivar fácilmente definiciones de estados entre sí. Definitivamente puede resolver problemas de escalabilidad de manera incremental.

Estado de búsqueda

Hasta ahora, hemos creado patrones y ejemplos para gestionar el estado internamente dentro de la aplicación. Pero no debemos ser ingenuos: casi nunca ocurre que una aplicación no necesite recuperar contenido o datos de API externas.

Para el estado del lado del cliente, nuevamente hay dos flujos de trabajo diferentes que necesitan reconocimiento:

  1. obteniendo los datos
  2. incorporando datos al estado de la aplicación

Al solicitar datos del lado del cliente, es importante tener en cuenta algunas cosas:

  1. la conexión de red del usuario: evite volver a buscar datos que ya están disponibles
  2. qué hacer mientras se espera la respuesta del servidor
  3. cómo manejar cuando los datos no están disponibles (error del servidor o falta de datos)
  4. cómo recuperarse si la integración se interrumpe (punto final no disponible, recurso cambiado, etc.)

Y ahora es cuando la cosa empieza a ponerse interesante. Esa primera viñeta, el elemento 1, está claramente relacionada con el estado de recuperación, mientras que el elemento 2 pasa lentamente hacia el estado de gestión. Los elementos 3 y 4 definitivamente pertenecen al alcance del estado de gestión, pero ambos dependen de la acción de recuperación y de la integración del servidor. La línea es definitivamente borrosa. Lidiar con todas estas piezas en movimiento es complejo y estos son patrones que no cambian mucho de una aplicación a otra. Cuando y como sea que obtengamos datos, debemos lidiar con esos 4 escenarios.

 

Afortunadamente, gracias a bibliotecas como React-Query y SWR, todos los patrones mostrados para el estado local se aplican sin problemas a los datos externos. Bibliotecas como estas manejan el caché localmente, por lo que siempre que el estado ya esté disponible pueden aprovechar la definición de configuración para renovar los datos o utilizarlos desde el caché local. Además, pueden incluso proporcionar al usuario datos obsoletos mientras actualizan el contenido y solicitan una actualización de la interfaz siempre que sea posible.

Además de esto, el equipo de React ha sido transparente desde una etapa muy temprana sobre las próximas API que tienen como objetivo mejorar la experiencia del usuario y del desarrollador en ese frente (consulte la documentación propuesta de Suspense aquí ). Gracias a esto, los autores de bibliotecas se han preparado para cuando lleguen dichas API y los desarrolladores pueden comenzar a trabajar con una sintaxis similar a partir de hoy.

Ahora, agreguemos un estado externo a nuestro MainUserManagementdiseño con SWR:

import { useSWR } from 'swr'import { UserInfoProvider } from '../context/user-info'import { ExtDataProvider } from '../context/external-data-provider'import { UserNavigationLayout } from '../layouts/user-navigation'import { ErrorReporter } from '../components/error-reporter'import { Loading } from '../components/loading'export const MainUserManagement = (page) = { const { data, error } = useSWR('/api/endpoint') if (error) = ErrorReporter {...error} / if (!data) = Loading / return ( UserInfoProvider ExtDataProvider UserNavigationLayout {page} /UserNavigationlayout /ExtDataProvider /UserInfoProvider )}

Como puede ver arriba, el useSWRgancho proporciona muchas abstracciones:

  • un buscador predeterminado
  • capa de almacenamiento en caché de configuración cero
  • manejador de errores
  • manejador de carga

Con 2 condiciones, podemos proporcionar retornos tempranos dentro de nuestro componente para cuando la solicitud falla (error) o mientras el viaje de ida y vuelta al servidor aún no ha finalizado (carga). Por estas razones, las bibliotecas están estrechamente vinculadas a las bibliotecas de la administración estatal. Aunque no son exactamente gestión de usuarios, se integran bien y nos proporcionan herramientas suficientes para simplificar la gestión de estos estados asincrónicos complejos.

Es importante enfatizar algo en este punto: una gran ventaja de tener una aplicación isomórfica es el ahorro de solicitudes para el back-end. Agregar solicitudes adicionales a su aplicación una vez que ya está en el lado del cliente afectará el rendimiento percibido. Aquí hay un excelente artículo (¡y un libro electrónico!) sobre este tema que profundiza mucho más.

Este patrón no pretende de ninguna manera reemplazar getStaticPropsni getServerSidePropsen las aplicaciones Next.js. Es otra herramienta más en el cinturón del desarrollador para construir cuando se le presentan situaciones peculiares.

Consideraciones finales

Mientras concluimos con estos patrones, es importante resaltar algunas advertencias que pueden asustarte si no eres consciente al implementarlas. Primero, recapitulemos lo que hemos cubierto en este artículo:

  • Contexto como forma de evitar el Prop Drilling;
  • API principales de React para gestionar el estado ( useStatey useReducer);
  • Pasar el estado del lado del cliente a través de una aplicación Next.js;
  • Cómo evitar que ciertas rutas accedan al estado;
  • Cómo manejar la obtención de datos en el lado del cliente para aplicaciones Next.js.

Hay tres compensaciones importantes que debemos tener en cuenta al optar por estas técnicas:

  1. A menudo es preferible utilizar métodos del lado del servidor para generar contenido estáticamente que obtener el estado del lado del cliente.
  2. La API de contexto puede provocar múltiples renderizaciones si no se tiene cuidado con el lugar donde se producen los cambios de estado.

Será importante considerar bien esos puntos; además, todas las buenas prácticas al tratar con el estado en una aplicación React del lado del cliente siguen siendo útiles en una aplicación Next.js. La capa del servidor puede ofrecer un aumento del rendimiento y esto por sí solo puede mitigar algunos problemas de cálculo. Pero también se beneficiará al seguir las mejores prácticas comunes cuando se trata de renderizar el rendimiento de las aplicaciones.

Inténtalo tú mismo

Puede consultar los patrones descritos en este artículo en vivo en nextjs-layout-state.netlify.app o consultar el código en github.com/atilafassina/nextjs-layout-state . Incluso puedes simplemente hacer clic en este botón para clonarlo instantáneamente en tu proveedor de Git elegido e implementarlo en Netlify:

En caso de que desee algo menos obstinado o simplemente esté pensando en comenzar con Next.js, existe este increíble proyecto inicial que lo ayudará a configurarlo para implementarlo fácilmente en Netlify. Nuevamente, Netlify hace que sea muy fácil clonarlo en su propio repositorio e implementarlo:

Referencias

  • Contexto y Redux: diferencias
  • Propuesta de contenedor Next.js
  • Diseños de Next.js
  • Jotai
  • Uso de React Context para la gestión de estados en Next.js

(vf, il)Explora más en

  • pila de mermelada
  • Siguiente.js
  • javascript





Tal vez te puede interesar:

  1. ¿Deberían abrirse los enlaces en ventanas nuevas?
  2. 24 excelentes tutoriales de AJAX
  3. 70 técnicas nuevas y útiles de AJAX y JavaScript
  4. Más de 45 excelentes recursos y repositorios de fragmentos de código

Gestión de estados en Next.js

Gestión de estados en Next.js

Reaccionar API principales para datosPropagación del estado local a través de la aplicaciónCrear una jerarquía entre rutasLos problemas crecientes vuelven

programar

es

https://pseint.es/static/images/programar-gestion-de-estados-en-next-1118-0.jpg

2024-05-28

 

Gestión de estados en Next.js
Gestión de estados en Next.js

Si crees que alguno de los contenidos (texto, imagenes o multimedia) en esta página infringe tus derechos relativos a propiedad intelectual, marcas registradas o cualquier otro de tus derechos, por favor ponte en contacto con nosotros en el mail [email protected] y retiraremos este contenido inmediatamente

 

 

Update cookies preferences