Original article: How to become a pro with React setState() in 10 minutes

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 funciona setState 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 toma una taza de café y sigue leyendo!

Conceptos básicos de setState( )

Los componentes de React le permiten dividir la interfaz de usuario (IU) en piezas independientes y reutilizables, para que pueda pensar en cada pieza de forma aislada.

Conceptualmente, los componentes son como funciones de JavaScript. Aceptan entradas arbitrarias (llamadas "propiedades" o "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 propiedades, 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 React deben actuar como funciones puras con respecto a sus propiedades. Esto significa funciones que nunca intentan cambiar sus entradas y siempre devuelven el mismo resultado para las mismas entradas.

Por supuesto, las interfaces de usuario de las aplicaciones son dinámicas y cambian con el tiempo. Por eso se creó state.

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 función disponible solo para los Componentes de clase.

setState es el método API proporcionado con la librería para que el usuario pueda definir y manipular el estado a lo largo del tiempo.

Tres reglas generales al usar setState( )


No modifiques el estado directamente

1
formas incorrectas y correctas de establecer el estado

Las actualizaciones de estado pueden ser asincrónicas

React puede agrupar varias llamadas setState() en una sola actualización para mejorar el rendimiento.

Debido a que this.props y this.state pueden actualizarse de forma asíncrona, no debe confiar en sus valores para calcular el siguiente estado.    

2
manipular el estado con un enfoque funcional

Siempre debe hacer este tipo de manipulación con un enfoque funcional, proporcionando el state y los props y devolviendo el nuevo state basado en el anterior.

Las actualizaciones de estado se fusionan

Cuando llamas a setState(), React fusiona el objeto que proporcionas en el resto del state actual.

En el siguiente ejemplo, estamos actualizando la variable dogNeedsVaccination independientemente de las otras variables de state.

La fusión es superficial, por lo que this.setState({ dogNeedsVaccination: true }) deja intactas las otras variables, reemplazando solo el valor de dogNeedsVaccination.

3

Respeta el flujo de datos y evita el estado al máximo

¡Los datos fluyen hacia abajo! Ni los componentes principales ni los secundarios pueden saber si un determinado componente tiene estado o no, y no debería importarles si se define como una función o una clase.

Es por eso que el state a menudo se llama local o encapsulado. No es accesible a ningún componente que no sea el que lo posee y lo configura.

Cuando estableces una propiedad de estado y lo usas en tu componente, estás interrumpiendo el flujo de las propiedades de renderizado. Si por alguna razón la propiead pasad a tu componente cambia en el componente padre, ¿el hijo no se volverá a renderizar automáticamente por arte de magia?

Veamos un ejemplo:

4

Aquí tienes un Componente Home que está generando un número mágico cada 1000ms y poniéndolo en su propio state.

Después de eso, representa el número e invoca a tres Componentes Child (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 de cascada de props de React y, considerando que el objetivo es solo mostrar el número mágico, está renderizando los props recibidos directamente.

5

Segundo enfoque

El Componente ChildOfHomeBrother recibe las props de su padre y, invocando componentDidMount, establece el número mágico en el state. Luego representa el state.magicNumber.

Este ejemplo no funcion porque render()no sabe que una prop ha cambiado, por lo que no activa la nueva representación del componente. Como el componente y no se vuelve a representar, no se invoca el componentDidMount y la pantalla no se actualiza.

6

Tercer enfoque

Por lo general, cuando intentamos que funcione con 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, agregamos componentDidUpdate para verificar si hay un cambio en props para activar la nueva representació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 aplicación grande 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 prop, siempre trate de mantener las cosas funcionando de acuerdo con el Flujo de React (Primer Enfoque).

Puede consultar una página web en funcionamiento con este ejamplo que he preparado para ti en  Glitch. ¿Echa un vistazo y diviértete?

Consulta también el código en  Home.js y  HomeCodeCleaned.js  (sin las cosas de HTML) en mi repositorio sobre este artículo.

Cómo establecer el estado con setState

Entonces, llegados a este punto, ¡creo que es hora de ensuciarse las manos!

¡Juguemos un poco con setState y mejoremos eso! Solo sigue y toma otra taza de café!

Vamos a crear un pequeño formulario para actualizar los datos del usuario:

7
pequeño ejercicio con setState()

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

8
Componente Home inicial

Estamos configurando el 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 (Last Name)?

9
Característica Last Name
10
handleFormChange abstraído

¡Bien! Hemos abstraído el método handleFormChange para poder manejar todos los campos de entrada y setState.

¿Qué pasa si agregamos un botón para marcar los datos como válidos o no válidos y un contador para saber cuántos cambios hemos hecho en el state?

11
captura de pantalla que muestra los console.log del estado del componente
12
handleFormChange actualizado con controladores de casillas de verificación y contadores

¡Sí! ¡Lo estamos petando! ¡Hemos abstraído muchas cosas!

Hmmm... Digamos que no quiero una casilla de verificación para controlar la variable, isValid 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 en las que React necesita cambios por lotes/grupos, no es una buena política confiar en la variable this.state.counter para agregar uno más. Este valor puede cambiar sin que te des cuenta.

Estamos usando una copia superficial del mismo 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 a ponernos un poco funcionales!

13
captura de pantalla mostrando el Toggle Valid/Invalid y los console.log de la variable state.counter
14
separación de los controladores

Vale – Hemos perdido la abstracción porque hemos separado los controladores, ¡pero es por una buena razón!

Así que en este momento mantenemos handleFormChange pasando un objeto al método API setState. 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 el estado de las variables que dependen del estado anterior.

¿Qué pasa si queremos cambiar el estado de console.log() de los formularios de entrada firstName y lastName cada vez que ocurre un cambio? ¡Hagamos un intento!

15
método logFields()

¡Muy bien! Cada vez que se produce handleFormChange (lo que significa que se ha pulsado una nueva tecla), se invoca el método logFields() y se registra el estado actual en la consola.

Comprobemos la consola del navegador:

16
captura de pantalla del estado de console.log de firstName y lastName

¡Espera! ¿Qué ha pasado aquí, amigo? ¡El registro de la consola muestra un cambio antes del cambio en el formulario actual! ¿Por qué está pasando esto?

¡¡setState es asíncrono!!

Ya lo sabíamos, ¡pero ahora lo estamos viendo con nuestros propios ojos! ¿Lo que está pasando allí? Echemos un vistazo a los métodos handleFormChange y logFields anteriores.

Entonces, el método handleFormChange recibe el nombre y el valor del evento, luego hace un setState de estos datos. Luego llama a handleCounter para actualizar la información del contador y, al final, invoca el método logFields. El método logFields toma el estado actual currentState y devuelve 'Eduard' en lugar de 'Eduardo'.

La cosa es: setState es asíncrono y no actúa en el momento. React está haciendo su trabajo y ejecuta primero el método logFields, dejando setState para el siguiente ciclo de eventos.

Pero, ¿cómo podemos evitar este tipo de situaciones?

Bien, la API setState tiene una callback (devolución de llamada) para evitar esta situación:

17
método API setState

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

18
utilizando el controlador de devolución de llamada del método setState() API

Bien, ¡ahora está funcionando!

Le decimos a React: “¡Oye, React! Ten cuidado de que cuando invoques el método logFields, quiero que tenga el estado ya actualizado, ¿de acuerdo? ¡Confío en ti!"

React dice: “¡Está bien, Edo! ¡Voy a encargarme de todo este montón de cosas que suelo hacer en el patio trasero con setState y solo cuando termine con eso invocaré logField()! Tranquilo colega! Relájate!”

19
screenshot of the console.log() of fullName

Y, de hecho, ¡funcionó!

¡Bien todos! En este momento hemos manejado los principales escollos de setState.

¿Tienes el coraje de ir más allá? Toma una taza de café y pongámonos realmente chulos.

Poniéndose elegantes con setState( )

Ahora que tenemos los métodos handleCounter y handleIsValid, y setState() expresado con funciones, ¡podemos componer la actualización de estado con otras funciones! ¡Me gusta la composición! ¡Vamos a divertirnos un poco!

20
abstrayendo handleIsValid

Podemos llevar la lógica dentro de setState a una función fuera del componente de clase. Llamémoslo toggleIsValid. ☝️

21
función toggleIsValid

Ahora esta función puede vivir fuera del componente de clase, en cualquier lugar de su aplicación.

¿Qué pasa si usamos una función de orden superior?

22
cambiando toggleIsValid por una función de orden superior

Guau! Ahora ya no estamos invocando la función toggleIsValid. Invocamos una función abstracta de orden superior llamada toggleKey y le pasamos una clave (una cadena en este caso).

¿Cómo necesitamos cambiar la función toggleIsValid ahora?

23
función de orden superior toggleKey

¡¿Qué?! Ahora tenemos una función llamada toggleKey que recibe una clave y devuelve una nueva función que cambia de estado según la clave suministrada.

Esta 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 a su opuesto.

¡Genial!

Hagamos lo mismo con el controlador del contador de incrementos:

24
abstracción handleCounter para invocar una función de orden superior

25
función de orden superior incrementCounter

‌¡Sí! ¡Funciona! Que guay! Vamos a volvernos locos ahora...

Disparando a la luna y regresando

¿Qué sucede si creamos una función makeUpdater 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? ¡Vamos!

26
función de orden superior makeUpdater

Ok, eso es suficiente... Nos paramos aquí

Puedes consultar todo el código que hemos hecho en este repositorio de GitHub.

Por último, pero no menos importante

No olvides evitar al máximo el uso del estado y respetar la cascada de propiedades de renderizado de React.

No olvides que setState es asíncrono.

No olvides que setState puede tomar un objeto o una función

No olvides que debes pasar una función cuando tu próximo estado dependa de su estado anterior.

¡Muchas gracias!