El concepto de estado en React: useState

El concepto de estado en React: useState

Entendiendo el concepto y funcionamiento de hooks y useState.

Introducción

Cuando hablamos de hooks, hablamos de una interfaz (colección de métodos y propiedades especiales) de la librería de React que nos permite trabajar con estados y otras funcionalidades dentro de los componentes (que hoy en día son funciones) puesto que los componentes funcionales por defecto no disponen de estado.

Antes, era necesario crear un componente usando clases para poder acceder a todas las funcionalidades de React.

El nombre hook se debe a que ahora podemos "enganchar" estas funciones especiales dentro de nuestros componentes funcionales.

**useState **es un Hook que te permite añadir estados a un componente funcional.

2 reglas de los Hooks

Hooks son funciones JavaScript especiales, pero para usarlas necesitamos seguir dos reglas.

Llamadas a Hooks solo desde nivel superior

No debemos llamar a los hooks dentro de loops,condicionales o funciones anidadas. En vez de ello siempre debemos usar los Hooks en el nivel más superior de nuestro Componente, antes de cualquier return .

Si seguimos esta regla, nos aseguramos que los Hooks se llamen en el mismo orden cada vez que un Componente se renderiza. Esto le permite a React preservar correctamente el estado de los hooks entre diferentes llamadas a useState y useEffect.

Llamadas a Hooks solo desde Componentes

No debemos llamar a Hooks desde funciones normales de JavaScript. En vez de eso, podemos:

  • Llamar a Hooks desde Componentes funcionales de React.

  • Llamar a Hooks desde un segundo Hook personalizado.

Al seguir esta regla, nos aseguramos que toda la lógica del estado de un componente sea claramente visible desde su código fuente.

Estado y useState

Como hemos visto en mi anterior artículo, cada vez que definimos datos/valores dentro de nuestro Componente funcional y que queremos que cambien, se mantenga un seguimiento de ellos , y se vuelvan a renderizar; debemos usar un estado.

Y dado que React por defecto no "escucha" a cambios en las variables locales, no sabe cuándo debe re-renderizar/guardar los datos/valores que cambian, por lo que debemos usar el Hook useState.

Mirémolos en en siguiente ejemplo:

import React,{ useState } from "react";

export default function App() {
    const estado= useState();
    console.log(estado)
    return (
        <div className="state">
            <h1 className="state--title">¿Es importante aprender estados?</h1>
            <div className="state--value">
                <h1>Si</h1>
            </div>
        </div>
    )
}

react state exercise.png

Lo primero que debemos hacer es importar useState, esto se puede hacer usando la desestructuración de objetos {useState} o accediendo directamente al método con React.useState();

En el ejemplo de arriba, ejecuto el método useState(), lo guardo en una variable de nombre estado y hago un console.log de la misma para ver qué nos devuelve:

array usestate.png

Si analizamos el resultado, nos damos cuenta que nos devuelve un array donde el primer item es una variable undefined y el segundo una función.

El primer item (variable) se llama variable de estado, esto quiere decir que sea lo que sea que le pasemos como argumento a useState, será el valor inicial de nuestro estado.

El segundo item (función) se llama función de estado, cada vez que queramos modificar la variable de estado debemos hacerlo a través de la función de estado, nunca la debemos cambiar directamente.

Normalmente la ejecutaremos siempre cuando una acción del usuario ocurra, por lo que usamos manejadores de eventos.

Dado que el método useState() nos devuelve un array, podemos desestructurarlo y asi usar tanto la variable de estado como la función de estado. Miremos un ejemplo donde asignamos un manejador de eventos a un elemento y cuando le hagamos clic la variable de estado cambie:

import React,{useState} from "react"

export default function App() {
    const [isImportant, setIsImportant] = useState(true)

    function handleClick() {
        setIsImportant(!isImportant);
    }

    return (
        <div className="state">
            <h1 className="state--title">¿Es importante aprender estados?</h1>
            <div className="state--value" onClick={handleClick}>
                <h1>{isImportant === true ? "Si" : "No"} </h1>
            </div>
        </div>
    )
}

state toggle.gif

Miremos un ejemplo ahora creando un contador:

import React,{useState} from "react"

export default function App() {

    const [contador, setContador] = useState(0);

    function incrementar(){
        setContador(contador+ 1);
    }

    function decrementar(){
        setContador(contador- 1);
    }


    return (
        <div className="counter">
            <button  onClick = {decrementar} className="counter--minus"></button>
            <div className="counter--count">
                <h1>{contador}</h1>
            </div>
            <button onClick = {incrementar}  className="counter--plus">+</button>
        </div>
    )
}

acounter state.gif

Aqui debemos prestar atención a un detalle, si nos damos cuenta, la variable contador no está siendo modificada usandocontador++ puesto que como hemos dicho más arriba, la variable de estado no debe ser modificada directamente y, usar contador++ es equivalente a contador = contador + 1.

Adicionalmente, no es una buena práctica usar la variable directamente dentro de la función de estado, es recomendable usar una función callback.

Cuando le pasamos una función callback a nuestra función de estado, la callback debe retornar el nuevo valor de nuestra variable de estado.

import React,{useState} from "react"

export default function App() {
    const [contador, setContador] = useState(0)

    function incrementar() {
        setContador(valorAnterior => valorAnterior + 1);
    }

    function decrementar() {
        setContador(valorAnterior => valorAnterior + 1);
    }

    return (
        <div className="counter">
            <button className="counter--minus" onClick={decrementar}></button>
            <div className="counter--count">
                <h1>{contador}</h1>
            </div>
            <button className="counter--plus" onClick={decrementar}>+</button>
        </div>
    )
}

Siempre que necesitemos referenciar al valor anterior de nuestro estado para determinar el nuevo valor, debemos pasar una función callback a la función de estado en vez de usarla directamente.

Esta función callback recibirá el valor anterior del estado como parámetro, el cual puede ser usado para determinar el nuevo valor.

En caso contrario, cuando no necesitemos referenciar al valor anterior de la variable de estado, simplemente pasamos el nuevo valor.

En mis próximos artículos abordaremos cómo trabajar con estados complejos como arrays u objetos, así que estate atento :))