Deja de abusar del manejo de estado del cliente

Rate this content
Bookmark

Muchas aplicaciones de React utilizan soluciones de manejo de estado como Redux o Mobx y las utilizan principalmente para el estado del servidor como 'isLoading', 'isError', etc. Necesitamos dejar de mezclar el estado del servidor y el estado del cliente. No me malinterpretes, el estado del cliente es importante, pero el 70% del estado es en realidad un estado del servidor. En esta charla demostraré cómo podemos encapsular el estado del servidor utilizando nuestro propio hook personalizado o utilizando una solución (perfecta) como react-query.

21 min
21 Jun, 2022

Video Summary and Transcription

Esta charla discute el abuso del manejo de estado y el uso de React Query para el manejo de API en aplicaciones de React. El orador demuestra la implementación de indicadores de carga, hooks personalizados, mecanismos de almacenamiento en caché y presenta React Query como una herramienta poderosa para obtener, almacenar en caché y cargar datos. La conclusión enfatiza que React Query simplifica el manejo de API sin la necesidad de herramientas complejas de manejo de estado como Redux o MobX.

Available in English

1. Introducción

Short description:

Hola a todos. Mi nombre es Nir. Tengo 34 años y soy de Israel, casado con una mujer y padre de tres maravillosas niñas. He construido aplicaciones web desde que era niño. Actualmente dirijo una empresa de consultoría en desarrollo de aplicaciones web, tanto en el mundo de la web 2.0 como en el mundo de la web 3.0.

Mi nombre es Nir. Tengo 34 años y soy de Israel, casado con una mujer y padre de tres maravillosas niñas, Baar y Adas. He construido aplicaciones web desde que era niño. Mi primer sitio web fue el sitio web más grande de fans de Pokémon en Israel y fue un gran proyecto. Desde entonces, obtuve mi licenciatura en Ciencias de la Computación en la Universidad Hebrea de Jerusalén. Trabajé como desarrollador full-stack en Intel durante un par de años. Después fui líder de equipo de frontend en Curv, una startup de blockchain que fue adquirida por PayPal. Actualmente dirijo una empresa de consultoría en desarrollo de aplicaciones web, tanto en el mundo de la web 2.0 con el que todos están familiarizados, como en el mundo de la web 3.0.

2. Abuso común de la gestión del estado

Short description:

Hablemos sobre el abuso común de la gestión del estado. Elegimos nuestra herramienta favorita de gestión del estado, conectamos los componentes a la funcionalidad de la API backend y comenzamos a utilizar la maravilla de la gestión del estado. Creé una aplicación de React, declaré una función de API llamada fetchPokemon y utilicé un servicio de API de Pokémon para obtener los datos relevantes. Agregué una demora de dos segundos, obtuve los datos e implementé la interfaz de usuario para mostrar los datos de Pokémon y un botón para obtener un Pokémon aleatorio. Por último, agregué un poco de estilo a la interfaz de usuario.

Basta de hablar de mí, hablemos sobre el abuso común de la gestión del estado. Todos hemos estado allí, comenzando una aplicación de React, eligiendo nuestra herramienta favorita de gestión del estado, como Redux o Morbix, u otras muchas disponibles, y conectando nuestros componentes utilizando estas herramientas con la funcionalidad de la API backend y comenzando a utilizar la maravilla de la gestión del estado.

Veámoslo en acción. He creado una sencilla aplicación de React utilizando Code Sandbox, y comenzaré declarando una función de API llamada fetchPokemon, que recibirá como parámetro el nombre del Pokémon, y utilizaremos un servicio de API de Pokémon para obtener los datos relevantes del Pokémon. Lo envolví con una promesa para asegurarnos de que haya una demora en la respuesta y simular un comportamiento común de las funciones de API. Así que hay un setTimeout, y lo configuré para una demora de dos segundos.

Ahora realizaré la obtención de los datos. Tenemos el nombre del Pokémon, manejamos la respuesta, que está en formato JSON, así que utilizaré el JSON y devolveré los datos. Genial. Muy bien. Añadamos la demora de dos segundos de la que hablé, y ahora puedo llamar al punto final en la interfaz de usuario, declarando el estado PokemonData y setPokemonData utilizando el hook useState, y creando una función fetch random Pokemon para que la aplicación no sea aburrida.

Así que elegiré entre tres Pokémon comunes, Pikachu, Charizard y Squirtle. Utilizaremos una lógica aleatoria utilizando la biblioteca math, multiplicando math random por la longitud del array menos 1 y redondeándolo. Y ahora puedo utilizarlo. Llamo a fetchPokemon con el nombre del Pokémon elegido. Genial. Devuelvo los datos. Muy bien. Y ahora viene la mejor parte, la interfaz de usuario. Así que declararé un contenedor div y mostraré los datos del Pokémon en un div y crearé un botón para utilizar la función fetch. En el div, mostraré el nombre del Pokémon, así como su imagen utilizando los sprites de los datos que obtenemos de la API del servicio. Lo pondré en el atributo source de la imagen. ¡Y genial! Volvamos al botón. Adjuntaré la función fetch random Pokemon que implementamos. Y agreguemos un poco de texto. Y agreguemos un texto alternativo para eliminar la molesta advertencia de Code Sandbox. Añadamos un pequeño error tipográfico aquí. Y probémoslo. ¡Genial! Tenemos a Squidward. Ahora agreguemos un poco de estilo, solo para mover el texto un poco hacia arriba.

3. Implementando Indicador de Carga

Short description:

¡Y hemos terminado! Tenemos a Toysard, Squirtle y Pikachu. Usaré useReducer por razones de simplicidad. Agreguemos un estado para los datos de error y un estado de éxito. Adaptaremos la interfaz de usuario para usar nuestra nueva variable de estado. Implementaremos fácilmente el indicador de carga. Corrijamos el error en el setTimeout.

¡Y hemos terminado! Tenemos a Toysard, Squirtle y Pikachu. ¡Genial! En mi opinión, una aplicación no es una aplicación sin un indicador de carga. Esa es la parte donde la mayoría de los desarrolladores usarán Redux u otra solución para manejar este tipo de estados.

Usaré useReducer por razones de simplicidad, que es muy similar a los reducers de Redux. Así que crearé un Reducer de Pokémon, que recibe un estado y una acción, y cambiará el tipo de acción. Primero, manejemos un caso predeterminado, agreguemos un error y ahora agreguemos el estado común de carga. El estado es de carga, y genial. Pero, ¿qué pasa si obtengo un error? Así que agreguemos un estado para esto también, con los datos de error, y seguramente necesitamos un estado de éxito para almacenar los datos obtenidos de la API del servicio. Y como a veces no estaremos en ninguno de estos estados, agreguemos un estado inactivo y declaremos un estado inicial para comenzar.

Ahora puedo usarlo en la aplicación, en lugar del useState regular. Así que extraigamos el estado y la función de despacho del reducer, usando el PokémonReducer y el estado inicial. Y adaptemos la interfaz de usuario para usar nuestra nueva variable de estado. Entonces, cuando esté inactivo, mostraré un texto para hacer clic y obtener un Pokémon, y en caso de éxito, quiero mostrar los datos del Pokémon. Así que implementemos esto también. Presta atención, el Pokémon data ya no existe, así que lo reemplazaremos con la variable de estado. Y en caso de error, quiero mostrar el mensaje de error que guardé en el estado. Y finalmente, quiero deshabilitar el botón mientras esté en el estado de carga.

Ahora finalmente puedo implementar fácilmente el indicador de carga. Entonces, lo que haré es cambiar el texto del botón a `Cargando` cuando esté en el estado de carga. Y mostraré el texto `Obtener Pokémon` cuando no esté. Obteniendo. Ok, y no olvides despachar el estado correcto cuando esté cargando. Y cuando recibamos los datos. Con los datos. Y cuando ocurra algún error. Actualicemos y probemos. Veo que no obtenemos el indicador de carga. Creo que sé por qué hay un error en el setTimeout. Arreglémoslo. Genial.

4. Implementando Hook Personalizado

Short description:

Probemos de nuevo y lo tenemos. Solo el estilo. Nos olvidamos del estilo. Así que movamos el estilo al div interno que implementamos. Por lo general, para este tipo de función asíncrona, los desarrolladores escriben diferentes reducers, pero conozco una mejor manera de compartir lógica. Hooks personalizados. Creemos uno. Tomemos el reducer, muévelo hacia abajo y declaremos un hook personalizado llamado userSync. Y ahora creemos una función llamada execute que será responsable de cambiar el estado usando el dispatch.

Probemos de nuevo y lo tenemos. Solo el estilo. Nos olvidamos del estilo. Así que movamos el estilo al div interno que implementamos. Y genial. Probemos de nuevo. Genial.

Por lo general, para este tipo de función asíncrona, los desarrolladores escriben diferentes reducers, pero conozco una mejor manera de compartir lógica. Hooks personalizados. Así que creemos uno. Tomemos el reducer, muévelo hacia abajo y declaremos un hook personalizado que llamaré userSync. Bien. El userSync recibirá una función asíncrona. Y de hecho, podemos usar nuestro reducer. Simplemente cambiémosle el nombre a algo más genérico. Tal vez fetchReducer sea mejor. Y podemos usarlo en nuestro hook. Genial.

Y ahora creemos una función que será responsable de cambiar el estado usando el dispatch. La llamaremos execute. Esta función recibirá parámetros, por supuesto. Comenzaremos despachando el estado de carga. Luego llamaremos a la función asíncrona argumento que recibimos con los parámetros. Y en caso de éxito, despacharemos el estado de éxito. Tenemos los datos, por supuesto. Y por último, pero no menos importante, en caso de error, despacharemos el estado de error, similar a lo que hicimos anteriormente. Quitamos el error con los datos del error. Ahora tendremos la función execute con useCallback para evitar re-renderizaciones, excepto cuando la función cambie. Y finalmente, devolvemos desde el hook la función execute y el estado, que podemos usar en la aplicación. Y vamos a usarlo de verdad.

5. Extrayendo Execute y State

Short description:

Vamos a extraer execute y state del hook personalizado userSync y pasarle nuestra función fetchRandomPokemon. Este patrón no es invención mía, sino algo que encontré en una biblioteca llamada userSync, que se implementa de manera muy similar a lo que hicimos. Es una biblioteca increíble con muchos otros hooks personalizados.

Entonces, vamos a extraer execute y state del hook personalizado userSync, y necesitamos pasarle nuestra función, fetchRandomPokemon. Y hagamos algunos ajustes. Movemos el dispatch y sacamos esta función de la aplicación. Llamamos a execute. Genial. Y esto funciona bien. En realidad, no inventé nada de esto. Solo uso un patrón que encontré en la biblioteca de la escuela, use hooks, y aquí puedes ver userSync, que se implementa de manera muy similar a lo que hicimos. Y en realidad es increíble y esta biblioteca tiene muchos otros hooks personalizados. Realmente sugiero que lo revises.

6. Implementando Mecanismo de Caché

Short description:

Vamos a agregar un mecanismo de caché para almacenar los datos obtenidos y implementar la caché. Si los datos existen en la caché, se devolverán. De lo contrario, se obtendrán utilizando una función asíncrona y se guardarán en la caché. También podemos agregar una invalidación para restablecer la caché cuando los datos no estén actualizados. Esto nos permite volver a obtener los datos cuando sea necesario, como cuando se conecta un temporizador.

Entonces, con tu permiso y para hacer un punto, voy a eliminar la aleatoriedad de los Pokémon por un momento y solo obtener al genial Pikachu. Observa que estoy obteniendo los data cada vez que hago clic, aunque tenga los mismos data todo el tiempo. Pikachu. Así que creo que sería bueno agregar un mecanismo de caché. Agregaré una variable de caché que contendrá los data y agregaré una bandera llamada caché y la llamaremos con true. Y vamos a implementar la caché. Entonces, si los data existen, los devolveré desde la caché. Correcto, si existen. Y de lo contrario, los obtendré utilizando la función asíncrona y los guardaré en la caché. Genial. Vamos a probarlo. Ahora se está obteniendo, y la segunda vez y la tercera vez y así sucesivamente, no se obtendrá nuevamente porque está utilizando los data en caché. Y vamos a agregar una invalidación para asegurarnos de que admitamos cuando los data no estén actualizados. Entonces, si la bandera de invalidación es true, restableceré la caché. Vamos a agregarlo al arreglo de dependencias. Y ahora vamos a establecer la bandera en true. Y si hago clic una y otra vez, se está obteniendo. Esto es útil cuando quiero indicarle a mi hook personalizado que los data ya no son relevantes y quiero volver a obtenerlos. Así que puedo conectarle un temporizador o algo así.

7. Introducción a React Query

Short description:

React Query es mi primera opción para manejar API LL. Proporciona un hook incorporado llamado Use Query que incluye todo lo que implementamos en UserSync. Podemos reemplazar UserSync con Use Query, pasando la clave de consulta y la función de consulta como parámetros. El resultado incluye datos, indicadores de error y una función de actualización. Con React Query, obtenemos la caché de forma predeterminada.

¿Y sabes qué? Es genial agregar un mecanismo de reintento. Y creo que sabes a dónde voy con esto. No tenemos que trabajar tan duro, aunque es divertido. Así que quiero que conozcas a React Query, que es mi primera opción cuando se trata de la API LL.

Primero, lo envolveré rápidamente con el proveedor de React Query. Viene de la biblioteca de React Query, que acabo de instalar. Y envolviendo toda la aplicación con el proveedor y el cliente. Y ahora puedo usar el hook incorporado de React Query llamado Use Query. Este hook contiene todo lo que acabamos de implementar en UserSync, incluido el sistema de caché, el mecanismo de reintento, la invalidación y muchas otras funcionalidades geniales.

Podemos deshacernos de UserSync y usar Use Query en su lugar. En realidad, tiene dos parámetros. El primero es la clave de consulta, que sirve como identificador de la consulta. Y el segundo es la función de consulta, similar al hook UserSync que acabamos de implementar. Entonces, esta será la función FetchPokemon, actualmente con Pikachu. Y vamos a extraer todo del resultado. Tenemos los data. Tenemos algunos indicadores, como si hubo un error, si está cargando, y así sucesivamente. Y lo usaremos en la interfaz de usuario, haremos algunos ajustes. Genial, el error. Podemos eliminar esto y usar la bandera IsError, que obtenemos de forma gratuita. Y la variable de error, que también podemos extraer. Y la función de actualización, que reemplazará la ejecución del hook UserSync. Genial, isLoading aquí en su lugar. Y aquí también. No IsLoading. Genial. Y genial, tenemos la caché de forma predeterminada. Limpiemos esto. Todo esto. Oh, qué buena sensación.

8. Conclusion and Recommendation

Short description:

Y incluso podemos usar la función fetch random Pokemon una vez más y obtenerla de la caché. Aquí está el sitio web de la biblioteca React Query. Tiene una documentación y funcionalidad geniales que hacen que el manejo de la capa de API sea simple. En conclusión, conectamos una función de API simple a una interfaz de usuario simple. A medida que la interfaz de usuario crece, el manejo de la funcionalidad de la API se vuelve más complicado. No necesitamos herramientas sofisticadas como Redux o MobX para el manejo del estado del servidor. React Query es una solución útil para obtener, almacenar en caché y cargar en cada aplicación de React.

Y incluso podemos usar la función fetch random Pokemon una vez más y obtenerla de la caché. Genial. Todo funciona.

Entonces aquí está el sitio web de la biblioteca React Query. Lo cual es muy genial. Tiene una documentación genial. Y realmente sugiero revisarla. Tiene muchas funcionalidades útiles y realmente simplifica las cosas relacionadas con el manejo de la capa de API. Así que realmente vale la pena revisarlo.

En conclusión, lo que hicimos aquí fue tomar una función de API simple y conectarla a una interfaz de usuario realmente simple. Pero como puedes imaginar, a medida que la interfaz de usuario se vuelve más grande y más compleja, se vuelve más complicado manejar este tipo de funcionalidad de API. Y no necesitamos herramientas sofisticadas como Redux, MobX o muchas otras disponibles para manejar lo que se llama estados del servidor. Estas herramientas están destinadas a operaciones de estado del cliente, como si el diálogo está abierto, si una barra de menú está abierta, si estoy conectado o no. Esto es estado del cliente. En cualquier caso de manejo de estado del servidor, como obtener, almacenar en caché, cargar, etc., realmente sugiero construir una solución similar a Use Query o usar hooks personalizados asíncronos que acabamos de ver, o considerar realmente usar React Query, que es realmente, realmente útil en cada aplicación de React que he visto hasta ahora. Así que espero que hayas tenido un buen tiempo. Yo lo pasé muy bien y sabes dónde encontrarme.

Check out more articles and videos

We constantly think of articles and videos that might spark Git people interest / skill us up or help building a stellar career

Vue.js London Live 2021Vue.js London Live 2021
34 min
Everything Beyond State Management in Stores with Pinia
Top Content
When we think about Vuex, Pinia, or stores in general we often think about state management and the Flux patterns but not only do stores not always follow the Flux pattern, there is so much more about stores that make them worth using! Plugins, Devtools, server-side rendering, TypeScript integrations... Let's dive into everything beyond state management with Pinia with practical examples about plugins and Devtools to get the most out of your stores.
React Advanced Conference 2022React Advanced Conference 2022
30 min
Using useEffect Effectively
Top Content
Can useEffect affect your codebase negatively? From fetching data to fighting with imperative APIs, side effects are one of the biggest sources of frustration in web app development. And let’s be honest, putting everything in useEffect hooks doesn’t help much. In this talk, we'll demystify the useEffect hook and get a better understanding of when (and when not) to use it, as well as discover how declarative effects can make effect management more maintainable in even the most complex React apps.
React Summit Remote Edition 2020React Summit Remote Edition 2020
30 min
React Query: It’s Time to Break up with your "Global State”!
Top Content
An increasing amount of data in our React applications is coming from remote and asynchronous sources and, even worse, continues to masquerade as "global state". In this talk, you'll get the lowdown on why most of your "global state" isn't really state at all and how React Query can help you fetch, cache and manage your asynchronous data with a fraction of the effort and code that you're used to.
React Day Berlin 2022React Day Berlin 2022
22 min
Jotai Atoms Are Just Functions
Top Content
Jotai is a state management library. We have been developing it primarily for React, but it's conceptually not tied to React. It this talk, we will see how Jotai atoms work and learn about the mental model we should have. Atoms are framework-agnostic abstraction to represent states, and they are basically just functions. Understanding the atom abstraction will help designing and implementing states in your applications with Jotai
React Advanced Conference 2023React Advanced Conference 2023
28 min
A Practical Guide for Migrating to Server Components
Server Components are the hot new thing, but so far much of the discourse around them has been abstract. Let's change that. This talk will focus on the practical side of things, providing a roadmap to navigate the migration journey. Starting from an app using the older Next.js pages router and React Query, we’ll break this journey down into a set of actionable, incremental steps, stopping only when we have something shippable that’s clearly superior to what we began with. We’ll also discuss next steps and strategies for gradually embracing more aspects of this transformative paradigm.
JSNation 2022JSNation 2022
27 min
Announcing Starbeam: Universal Reactivity
Starbeam is a library for building reactive data systems that integrate natively with UI frameworks such as React, Vue, Svelte or Ember. In this talk, Yehuda will announce Starbeam. He will cover the motivation for the library, and then get into the details of how Starbeam reactivity works, and most importantly, how you can use it to build reactive libraries today that will work natively in any UI framework. If you're really adventurous, he will also talk about how you could use Starbeam in an existing app using your framework of choice and talk about the benefits of using Starbeam as the state management system in your application.

Workshops on related topic

React Summit 2020React Summit 2020
96 min
Rethinking Server State with React Query
Top Content
Featured Workshop
The distinction between server state and client state in our applications might be a new concept for some, but it is very important to understand when delivering a top-notch user experience. Server state comes with unique problems that often sneak into our applications surprise like:
- Sharing Data across apps- Caching & Persistence- Deduping Requests- Background Updates- Managing “Stale” Data- Pagination & Incremental fetching- Memory & Garbage Collection- Optimistic Updates
Traditional “Global State” managers pretend these challenges don’t exist and this ultimately results in developers building their own on-the-fly attempts to mitigate them.
In this workshop, we will build an application that exposes these issues, allows us to understand them better, and finally turn them from challenges into features using a library designed for managing server-state called React Query.
By the end of the workshop, you will have a better understanding of server state, client state, syncing asynchronous data (mouthful, I know), and React Query.
React Summit Remote Edition 2021React Summit Remote Edition 2021
71 min
State Management in React with Context and Hooks
WorkshopFree
A lot has changed in the world of state management in React the last few years. Where Redux used to be the main library for this, the introduction of the React Context and Hook APIs has shaken things up. No longer do you need external libraries to handle both component and global state in your applications. In this workshop you'll learn the different approaches to state management in the post-Redux era of React, all based on Hooks! And as a bonus, we'll explore two upcoming state management libraries in the React ecosystem.