Para ilustrar mejor que es el estado, podemos imaginar un formulario de registro o actualización de empleados. El cual estaría en modo edición cuando se trate de un nuevo empleado o pongamos al empleado existente en modo edición, pero si solo queremos consultar su información, entonces estaría en modo solo lectura:
En este ejemplo podemos apreciar la importancia del estado, pues el estado contendrá los datos del empleado y una bandera booleana que indique si el formulario está en modo edición. Si el componente está en modo lectura, entonces solo mostrara los datos del empleado en etiquetas <label>, pero si pasamos a edición, entonces los <label> se remplazan por etiquetas <input>, y una vez que guardamos los datos, entonces podemos pasar el formulario nuevamente a modo solo lectura.
Como podemos apreciar, los estados permiten representar la información en pantalla, pero al mismo tiempo, puede repercutir en la forma en que el componente muestra la información y se comporta. En este ejemplo, utilizamos el estado para guardar los datos del empleado, como son nombre, edad, teléfono y fecha de nacimiento, pero al mismo tiempo, utilizamos el estado para determina como es que el componente de se debe de mostrar al usuario.
Este fragmento de código, representa el estado de la pantalla anterior, y como podemos observar, es un objeto JavaScript común y corriente, el cual tiene la información del empleado (líneas 3 a 8), pero por otro lado, tiene una propiedad que sirven exclusivamente para determinar la forma de mostrar la información, como es el caso de la propiedad editMode (línea 2).
Los estados pueden tener la estructura que sea y también pueden ser tan grandes y complejos como nuestra interface lo requieres, de tal forma que podemos tener muchos más datos y muchos más campos de control de interface gráfica.
Establecer el estado de Componente
Existe dos formas de establecer el Estado de un componente; definiéndolo en el constructor del componente mediante la asignación directa a la propiedad this.state o establecerlo dinámicamente en cualquier función o evento, mediante la función setState().
Si lo que buscamos es inicializar el estado antes de realizar cualquier otra acción, podemos hacerlos directamente sobre el constructor, por ejemplo:
En este ejemplo, definimos el estado del componente TweetsContainer mediante una lista de tweets vacío, esto con la finalidad de que cuando el componente se muestre por primera vez, no marce un error debido a que el estado es Null.
this.state solo funciona en el constructor
Es muy importante resaltar que establecer el Estado directamente mediante la instrucción this.state solo se puede hacer en el constructor, ya que de lo contrario, provocaría que React no sea capaz de detectar los cambios y es considerada una mala práctica.
El segundó método, es establecer el estado mediante la función setState(), la cual se utiliza para establecer un nuevo estado al componente. Al hacer esto, estamos sobrescribiendo el estado anterior, lo que hará que nuestro componente se actualice con los valores del nuevo estado.
Es normal que durante la vida de un componente establezcamos el estado varias veces a medida que interactuamos con la interface, o simplemente cuando cargamos datos del backend.
Si recordamos en el componente TweetsContainer, definimos la función loadTweets, la cual cargaba los Tweets del API y luego actualizaba el Estado del componente mediante this.setState(). Recordemos como quedo esta parte:
1loadTweets(username, onlyUserTweet){2let url ='/tweets'+(onlyUserTweet ?"/"+ username :"")3APIInvoker.invokeGET(url,response=>{4this.setState({5tweets: response.body6})7},error=>{8console.log("Error al cargar los Tweets", error);9})10}
El estado es actualizado cuando obtenemos la respuesta del API (línea 4). Cuando actualizamos el estado mediante la función this.setState(), React automáticamente actualiza el componente, para que de esta forma, la vista sea actualizada para reflejando el nuevo Estado.
Actualizando el estado de un Componente
Una de las principales diferencias que existe entre las propiedades (Props) y el Estado, es que el estado está diseñado para ser mutable, es decir, podemos realizar cambios en el, de tal forma que los componentes puedan ser interactivos y responder a las acciones del usuario.
Cuando React detecta la actualización del estado en uno de sus componentes, este inicia una actualización en cascada de todos los componentes hijos, para asegurarse de que todos los componentes sean actualizados con el nuevo estado.
Para actualizar el Estado de un componente, se utiliza la función setState(), por lo que en realidad no existe una diferencia entre actualizar el Estado y establecer su valor inicial, sin embargo, la diferencia radica en la forma en la forma en que modificamos el Objeto asociado al estado.
React está pensado para que el objeto que representa el estado sea inmutable, por lo que cuando creamos un nuevo estado, tendremos que hacer una copia del estado actual y sobre esa copia agregar o actualizar los nuevos valores. Esto es así para garantiza que React pueda detectar los nuevos cambios y actualizar la vista.
Una de las formas que tenemos para actualizar el estado, es mediante el método Object.assign, el cual se utiliza para copiar los valores de todas las propiedades enumerables de uno o más objetos fuente a un objeto destino. Retorna un nuevo objeto con los cambios. Veamos un ejemplo:
En este ejemplo, vamos a asumir que el estado actual del componente es un objeto que solo tiene la propiedad edit=false y queremos cambiar el valor edit=true, para hacer esto, nos apoyamos de Object.assign (línea 4) que en este caso recibe 3 parámetros, el primero será el objeto al cual deberá aplicar los cambios, por lo que el valor indica que es un objeto nuevo. El segundo parámetro es el estado actual del componente (this.state). Como tercer parámetro enviamos el objeto newState, el cual contiene los nuevos valores para el estado. Como resultado de la asignación se realizará una “merge” (mescla) entre el estado actual y el nuevo estado, por lo que los valores que ya están en el estado actual solo se actualizarán y los que no estén, se agregarán. Finalmente, actualizamos el estado mediante this.setState().
Tras ejecutar este código podremos ver en el log del navegador como quedaría la variable stateUpdate:
Actualizar el estado con Object.assign puede resultar bastante simple, y lo es, pero tiene un problema fundamental, y es que no es capaz de realizar copias a profundidad, esto quiere decir que, si nuestro estado tiene otros objetos dentro, este no realizará una clonación de estos objetos, en su lugar, los pasará como referencia.
Un ejemplo claro de este problema es cuando nuestro objeto tiene una array, veamos otro ejemplo:
Veamos que hemos agregar al estado una lista de tweets (línea 1 a 7), luego aplicada la asignación (línea 8). Finalmente agregamos un nuevo Tweet al objeto newState. (línea 9). Uno esperaría que el objeto newState tenga el tweet 3, mientras que el stateUpdate se quedaría con los dos primero. Sin embargo, la realidad es otra; veamos el resultado de log (línea 10) tras ejecutar este código:
Si esto lo hiciéramos de esta forma, tuviéramos un problema con React, pues podríamos modificar el Array desde el otro objeto y React no podría detectar los cambios. Para solucionar este problema, existe una librería más sofisticada para actualizar los estados de una forma más segura, la cual veremos a continuación.
Acerca de este libro
Todo lo que acabas de ver en este artículo es solo una pequeña parte del libro Aplicaciones reactivas con React, NodeJS & MongoDB, El libro más completo en español para aprender a crear aplicaciones web completas con las tecnologías más potentes de la actualidad, desde el Frontend con React, hasta el Backend con un poderoso API REST con NodeJS y Express y persistiendo todo en MongoDB. te invito a que veas mi libro: