Hoy te traemos una pequeña píldora de conocimiento de React, que descubrimos a raíz de un problema que tuvimos en un proyecto en el que había 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 porque necesitamos el estado anterior para no sobreescribirlo con información errónea.
Por aquí te dejamos un repositorio 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 de setState. También está el ejemplo de al lado de cómo sería aplicando la solución que mencionamos más abajo.
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 dispara en un momento dado, pero no podemos saber en qué instante se aplica realmente. Por tanto, no tenemos certeza de que el “estado anterior” no pueda modificarse desde que se lanza el evento de actualización hasta que finalmente se escribe el nuevo valor.

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 de 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 real previo.
const updateStateWithoutPreviousStateValue = (newStateValue) => {
setStateValue({ ...stateValue, ...newStateValue })
}
Tras buscar bastante por Internet, llegamos a la documentación oficial de React, donde existe una forma de hacer que la modificación de un estado dependa siempre del estado anterior. No del valor que tenemos en la variable expuesta, sino del último seteo que se ha aplicado realmente. Es tan sencillo como usar el mismo setState de antes, pero pasándole como argumento una función que recibirá como parámetro el estado anterior.
const [stateValue, setStateValue] = React.useState({})
// De esta manera sí garantizamos que estamos cogiendo el estado previo real.
const updateStateBasedOnPreviousStateValue = (newStateValue) => {
setStateValue((previousStateValue) => {
return { ...previousStateValue, ...newStateValue }
})
}
Finalmente, este pequeño reto nos hizo comprender mejor el funcionamiento de React, enfrentarnos a una situación algo diferente y aprender de ella.