Este artículo está dirigido a personas que ya han tenido su primer acercamiento a React, y que, como principiantes, tienen dudas sobre cómo setState
funciona y cómo usarlo correctamente. También debería ayudar a los desarrolladores de nivel medio a superior a usar formas más limpias y abstractas de establecer el estado, y hacer que las funciones de orden superior manejen y abstraigan el estado.
¡Solo lee y diviértete!
¡Así que tómate una taza de café y sigue leyendo! ?
Conceptos básicos de setState ()
Los componentes de React te permiten dividir la interfaz de usuario (UI) en piezas independientes y reutilizables, para que puedas pensar en cada pieza de forma aislada.
Conceptualmente, los componentes son como funciones de JavaScript. Aceptan entradas arbitrarias (llamadas "props") y devuelven elementos React que describen lo que debería aparecer en la pantalla.
Si necesita darle al usuario la oportunidad de ingresar algo o de alguna manera cambiar las variables que el componente recibe como accesorios, necesitará setState
.
Ya sea que declare un componente como una función o una clase, nunca debe modificar sus propios accesorios.
Todos los componentes de Reactdeben actuar como funciones puras con respecto a sus accesorios. Esto significa funciones que nunca intentan cambiar sus entradas y siempre devuelven el mismo resultado para las mismas entradas.
Por supuesto, las IU de las aplicaciones son dinámicas y cambian con el tiempo. Por eso state
fue creado.
State
permite que los componentes de React cambien su salida con el tiempo en respuesta a las acciones del usuario, las respuestas de la red y cualquier otra cosa, sin violar esta regla.
Los componentes definidos como clases tienen algunas características adicionales. El estado local es una característica disponible solo para la clase Componentes.
setState
es el método API que se proporciona con la biblioteca para que el usuario pueda definir y manipular el estado a lo largo del tiempo.
Tres reglas generales al usar setState ()
No modifique el estado directamente

Las actualizaciones de estado pueden ser asincrónicas
React puede agrupar varias setState()
llamadas en una sola actualización para mejorar el rendimiento.
Debido a que this.props
y this.state
pueden actualizarse de forma asincrónica, no debe confiar en sus valores para calcular el siguiente estado.

Siempre se debe hacer este tipo de manipulación con un enfoque funcional, el suministro state
y props
y volviendo el nuevo state
basado en el anterior.
Las actualizaciones de estado se fusionan
Cuando llamas setState()
, React fusiona el objeto que proporcionas con el actual state
.
En el siguiente ejemplo, estamos actualizando la variable dogNeedsVaccination
independientemente de las otras state
variables.
La fusión es superficial, por lo que this.setState({ dogNeedsVaccination: true })
deja las otras variables intactas, reemplazando solo el valor de dogNeedsVaccination
.

Respete el flujo de datos y evite el estado máximo
¡Los datos fluyen hacia abajo! Ni los componentes padre ni hijo pueden saber si un determinado componente tiene estado o no, y no debería importarles si está definido como una función o una clase.
Es por eso state
que a menudo se le llama local o encapsulado. No es accesible para ningún componente que no sea el que lo posee y lo configura.
Cuando setState
utiliza un accesorio y lo usa en su componente, está interrumpiendo el flujo de los accesorios de renderizado. Si por alguna razón el accesorio pasado a su componente cambió en el componente principal, ¿el niño no volverá a renderizarse automáticamente?
Veamos un ejemplo:

Aquí tienes un Home
Componente que genera un número mágico cada 1000ms y lo configura por sí solo state
.
Después de eso, renderiza el número e invoca tres Child
Componentes (hermanos) que recibirán el número mágico con el objetivo de mostrarlo usando tres enfoques diferentes:
Primer enfoque
El componente ChildOfHome
está respetando el flujo en cascada de los accesorios de React y, considerando que el objetivo es solo mostrar el número mágico, está renderizando lo props
recibido directamente.

Segundo enfoque
El componente ChildOfHomeBrother
recibe el props
de su padre y, al invocarlo componentDidMount
, establece el número mágico en state
. Luego genera el state.magicNumber
.
Este ejemplo no funciona porque render()
no sabe que prop
ha cambiado, por lo que no activa la reproducción del componente. Como el componente ya no se vuelve a renderizar, componentDidMount
no se invoca y la visualización no se actualiza.

Tercer enfoque
Por lo general, cuando intentamos que funcione utilizando el segundo enfoque, pensamos que falta algo. ¡En lugar de dar un paso atrás, seguimos agregando cosas al código para que funcione!
Entonces, en este tercer enfoque, hemos agregado componentDidUpdate
para verificar si hay un cambio props
para activar la reproducción del componente. Esto es innecesario y nos lleva a un código sucio. También trae consigo costos de rendimiento que se multiplicarán por la cantidad de veces que hagamos esto en una gran aplicación donde tenemos muchos componentes encadenados y efectos secundarios.
Esto es incorrecto a menos que necesite permitir que el usuario cambie el valor de prop recibido.
Si no necesita cambiar el valor de la propiedad, intente siempre que las cosas funcionen de acuerdo con el flujo de React (primer enfoque).
Puede consultar una página web que funcione con este ejemplo que he preparado para usted en Glitch. ¿Echa un vistazo y diviértete?
También consulte el código en Home.js
y HomeCodeCleaned.js
(sin el material HTML) en mi repositorio sobre este artículo.
Cómo configurar Estado
¡Así que en este punto creo que es hora de ensuciarnos las manos!
¡Juguemos un poco setState
y mejoremos eso! ¡Solo síguelo y toma otra taza de café!
Creemos un pequeño formulario para actualizar los datos del usuario:

Aquí está el código del ejemplo anterior:

Estamos configurando state
como un objeto, y no hay problema porque nuestro estado actual no depende de nuestro último estado.
¿Qué pasa si creamos un campo de formulario más para introducir y mostrar el apellido?


¡Agradable! Hemos abstraído el handleFormChange
método para poder manejar todos los campos de entrada y setState
.
¿Qué pasa si agregamos un botón de alternancia para marcar los datos como válidos o no válidos y un contador para saber cuántos cambios hemos realizado en el estado?


¡Si! ¡Estamos rockeando! ¡Hemos resumido muchas cosas!
Hmmm… Digamos que no quiero una casilla de verificación para controlar la isValid
variable, sino un simple botón de alternancia.
También separemos el controlador de contador de este método. Funciona bien, pero en situaciones más complejas donde React necesita realizar cambios por lotes / grupos, no es una buena política confiar en la this.state.counter
variable para agregar una más. Este valor puede cambiar sin que usted se dé cuenta.
Estamos usando una copia superficial en el instante en que se invoca la operación, y en ese momento determinado no sabes si su valor es el que esperabas o no.
¡Vamos un poco funcionales!


Bien, hemos perdido la abstracción porque hemos separado los controladores, ¡pero es por una buena razón!
Entonces, en este momento, mantenemos el handleFormChange
paso de un objeto al setState
método API. Pero los métodos handleCounter
y handleIsValid
ahora son funcionales y comienzan tomando el estado actual y luego, dependiendo de ese estado, cambiándolo al siguiente.
Esta es la forma correcta de cambiar las state
variables que dependen del estado anterior.
¿Qué pasa si queremos console.log()
indicar cambios en los formularios de entrada firstName
y lastName
cada vez que ocurre un cambio? ¡Hagamos un intento!

¡Agradable! Cada vez que handleFormChange
ocurre (lo que significa que ocurrió una nueva pulsación de tecla) el logFields()
método se invoca y registra el estado actual en la consola.
Revisemos la consola del navegador:

¡Espere! ¿Qué pasó aquí amigos? ¡El registro de la consola es un cambio antes de la entrada del formulario actual! ¿Por qué está pasando esto?
setState es asincrónico !!
¡Ya lo sabíamos pero ahora lo estamos viendo con nuestros ojos! ¿Lo que está sucediendo allí? Echemos un vistazo a los métodos handleFormChange
y logFields
anteriores.
Entonces, el handleFormChange
método recibe el nombre y el valor del evento, luego hace una parte setState
de estos datos. Luego llama al handleCounter
para actualizar la información del contador y, al final, invoca el logFields
método. El logFields
método toma el currentState
y devuelve 'Eduard' en lugar de 'Eduardo'.
La cosa es: setState
es asincrónica y no actúa en el momento. React está haciendo su trabajo y ejecuta el logFields
método primero, dejando setState
para el siguiente ciclo de eventos.
Pero, ¿cómo podemos evitar este tipo de situaciones?
Bueno, la setState
API tiene una función callback
para evitar esta situación:

Si queremos que tengamos logFields()
en cuenta los cambios recientes que hemos realizado en el estado, debemos invocarlo dentro de la devolución de llamada, así:

¡Bien, ahora está funcionando!
Le estamos diciendo a React: “¡Oye, React! Ojo que cuando invoques el logFields
método quiero que tengas el state
ya actualizado ¿ok? ¡Confío en ti!"
Reaccionar dice: “¡Está bien Edo! Voy a manejar todo este lote de cosas que suelo hacer en el patio trasero con la setState
cosita y solo cuando termine con eso, ¡invocaré logFields()
! ¡Hombre genial! ¡Relajarse!"

Y, de hecho, ¡funcionó!
¡De acuerdo a todos! En este momento hemos manejado las principales trampas de setState
.
¿Tienes el coraje de traspasar el muro? Tómate una taza de café y pongámonos en marcha ...
Ponerse elegante con setState ()
Ahora que tenemos los métodos handleCounter
y handleIsValid
, y setState()
expresados con funciones, ¡podemos componer la actualización de estado con otras funciones! Me gusta la composición! ¡Vamos a divertirnos un poco!

Podemos llevar la lógica interna setState
a una función fuera del componente de la clase. Vamos a llamarlo toggleIsValid
. ☝️

Ahora, esta función puede vivir fuera del componente de la clase, en cualquier lugar de su aplicación.
¿Qué pasa si usamos una función de orden superior?

¡Guauu! Ahora ya no invocamos la toggleIsValid
función. Estamos invocando una función abstracta de orden superior llamada toggleKey
y pasándole una clave (cadena en este caso).
¿Cómo necesitamos cambiar la toggleIsValid
función ahora?

¡¿Qué?! Ahora tenemos una función llamada toggleKey
que recibe key
y devuelve una nueva función que cambia de estado de acuerdo con la clave proporcionada.
Esto toggleKey
puede estar en una biblioteca o en un archivo auxiliar. Se puede invocar en muchos contextos diferentes para cambiar el estado de lo que quieras por su opuesto.
¡Excelente!
Hagamos lo mismo con el controlador de contador de incrementos:


¡Si! ¡Funciona! Tan agradable. Vamos a volvernos locos ahora ...
Disparar a la luna y volver
¿Qué pasa si creamos una makeUpdater
función genérica que recibe la función de transformación que desea aplicar, toma la clave y devuelve la función de estado que administra el estado con la función de transformación y la clave? ¿Un poco confundido? ¡Vamonos!

Ok, eso es suficiente… Detengámonos aquí. ?
Puede verificar todo el código que hemos hecho en este repositorio de GitHub.
Por último, si bien no menos importante
No olvides evitar el uso máximo del estado y respetar la cascada de accesorios de renderizado de React.
No olvides que setState
es asincrónico.
No olvides que setState
puedes llevar un objeto o una función.
No olvide que debe pasar una función cuando su próximo estado dependa de su estado anterior.
Bibliografía
- Reaccionar la documentación
- Reach Tech Courses de Ryan Florence, que realmente recomiendo.
¡Muchas gracias!