leanmind logo leanmind text logo

Blog

Refactorización Avanzada

Dominar el refactoring productivo para maximizar el retorno de la inversión.

Modificación concurrente de estado en React

Por Jorge Aguiar Martín y Adrián Ferrera González

Hoy te traemos una píldora de conocimiento de React, que descubrimos en base a un problema que tuvimos en un proyecto en el que teníamos concurrencia en el frontend.

Contexto

En nuestro caso, teníamos un estado dentro de un contexto, del que varias pantallas necesitaban obtener información y en el cual varios “procesos” realizaban una actualización simultánea del mismo. El problema está en que, si dichos procesos hacen la actualización del estado a la vez, podemos tener inconsistencias ya que necesitamos el estado anterior para no sobreescribir con información errónea el mismo.

Por aquí te dejamos un repo en el que, si lo ejecutamos y le damos al botón de start, veremos la inconsistencia que se genera en el estado al intentar hacer la actualización de manera convencional, incluso si considerásemos el estado anterior dentro del setState. También está el ejemplo al lado de como sería, adoptando la solución que más abajo mencionamos.

Problema

El principal causante de esto es que React ha diseñado los setState de manera asíncrona, es decir, es un evento que se hace trigger en un momento dado, pero no podemos saber en que momento se ha lanzado. Por tanto, no tenemos certeza de que el “estado anterior” no se pueda modificar desde el momento que se lanza el evento de seteo, hasta que finalmente se modifica el mismo.

react-concurrent-diagram

Solución

Como el problema es que los seteos son asíncronos, debemos tener algún mecanismo por el cual podamos hacer que, o bien sean síncronos, o dependan unos de otros.

Sin conocer a fondo React, la primera solución que se nos viene a la cabeza es usar dentro del setState el valor del estado que tenemos para sobreescribir el mismo. Nos daríamos cuenta enseguida de que esto realmente no funciona.

const [stateValue, setStateValue] = React.useState({})

// Esto NO nos garantiza que el valor del estado sea el "previo"
const updateStateWithoutPreviousStateValue = (newStateValue) => {
  setStateValue({ ...stateValue, ...newStateValue })
}

Tras hacer búsqueda intensiva en Internet, llegamos a la documentación oficial de React, en la que existe la manera de que dicha modificación de un estado dependa siempre del estado anterior, (no el que nosotros tenemos en la variable expuesta, sino del último seteo que se ha hecho del mismo). Es tan sencillo como usar el mismo setState de antes, pero pasándole como argumento una función que tendrá como parámetro el que será el estado anterior, para poder depender de él.

const [stateValue, setStateValue] = React.useState({})

// De esta manera SI garantizamos que estamos cogiendo el valor 
// "previo" del estado
const updateStateBasedOnPreviousStateValue = (newStateValue) => {
  setStateValue((previousStateValue) => {
    return { ...previousStateValue, ...newStateValue }
  })
}

Finalmente, este pequeño reto nos ha hecho comprender mejor el funcionamiento de React, así como enfrentarnos a una situación un tanto diferente y aprender de ella.

Publicado el 15/07/2024 por
Jorge image

Jorge Aguiar Martín

Adrián image

Adrián Ferrera González

https://adrianferrera.dev

¿Quieres más? te invitamos a suscribirte a nuestro boletín para avisarte cada vez que recopilemos contenido de calidad que compartir.

Si disfrutas leyendo nuestro blog, ¿imaginas lo divertido que sería trabajar con nosotros? ¿te gustaría?

Impulsamos el crecimiento profesional de tu equipo de developers