DEV Community

Emmanuelle Laguna
Emmanuelle Laguna

Posted on

React Hooks Parte 1 - Entendiendo el estado a través de los Hooks.

¿Para qué se usan los Hooks?

En 2019 React introdujo los Hooks a su API. Esto naturalmente causo revuelo, ya que hasta ese momento no se podía manejar el estado en componentes que no fueran de tipo clase. Esto suponía una molestia ya que escribir componentes como funciones es más sencillo que hacerlo como clases, ademas en muchos casos los componentes no necesitan lógica avanzada y crear una clase para manejar un estado sencillo, se volvía tedioso. Veamos un componente simple declarado como clase y comparemos su equivalente escrito como función.

Componente como clase

export default class MyComponent extends Component {
    render() {
        return (
            <h1>A simple component as class</h1>
        );
    }
}

Componente como función

export default MyComponent = () => <h1>A Simple component as function</h1>;

Como podemos ver, aún en componentes simples escribirlos como clases supone una sintaxis más compleja, aunque conviene no dejar de lado que tener clases tenía ciertas ventajas sobre los componentes escritos como funciones y entre esas ventajas se encontraba el uso de estados, ademas de poder controlar el ciclo de vida de los componentes. Antes de entrar de lleno a los Hooks explicaré que es el estado de un componente y para qué sirve.

El estado de un componente.

Si eres nuevo en React, seguramente habrás notado que el estado siempre se menciona, ya que para quienes hacemos web, hacer que el DOM cambie o que se ejecute una función a partir de una acción del usuario en el navegador es cotidiano. Con JavaScript existen formas de hacer esto, pero sí estamos usando React, podemos aprovechar su poder y hacerlo de forma más legible y en muchos casos con mejor rendimiento. Pensemos en un componente que se conectará a una API y después mostrará una lista. Si nos preocupamos por nuestros usuarios, es una buena práctica de experiencia de usuario el avisar, por lo menos con un texto, que los datos están cargando, ya que en conexiones rápidas, seguramente el usuario no notará nunca la carga, pero en conexiones lentas, puede que la carga de datos tarde un poco o incluso podría haber un error de conexión. Está es una necesidad común de hecho y resolverlo a través de estados es muy sencillo. Veamos cómo se resuelve esto a través de un componente tipo clase.

export default class MyComponent extends Component {
  constructor() {
    this.state = {
      dataState: "loading",
      message: "Cargando mensaje...",
    };
  }

  componentDidMount() {
    const { dataState } = this.state;
    if (dataState === "loading") {
      fetchToServer()
        .then((message) =>
          this.setState({
            dataState: "loaded",
            message,
          })
        )
        .catch(() =>
          this.setState({
            dataState: "error",
            message: "Error de conexión.",
          })
        );
    }
  }

  render() {
    const { message } = this.state;
    return <p>{message}</p>;
  }
}

Lo primero que debemos notar en nuestro componente es que estamos declarando un mensaje por defecto, el cual indica al usuario que estamos cargando el mensaje. Por lo tanto mientras no se cambie ese mensaje a través del método setState el mensaje a mostrarse será “Cargando mensaje…”. La segunda parte del componente es el componentDidMount, el cual es ejecutado una vez que el componente fue cargado. Lo primero que hacemos es validar que no hemos intentado ya cargar los datos y por ello comparamos que el valor de dataState sea el que tenemos por defecto en el constructor, es decir “loading”. Después, suponemos que fetchToServer es una promesa que retorna la respuesta del servidor y por ello, si la petición fue exitosa vamos a cambiar el estado a “loaded” y ademas pasar el mensaje que nos regreso la promesa. Por otro lado si algo salió mál, pasaremos el mensaje de error. Sin importar si la petición fue exitosa o no, estamos dando retroalimentación a nuestro usuario.

Como podemos ver en el ejemplo anterior el uso del estado es muy importante y como ya imaginarás, tiene muchos usos prácticos, pues a través de él se puede controlar el comportamiento de un componente. Esto anterior si eres un usuario experimentado te sonará familiar y si eres nuevo seguramente te hará sentido, pero gracias a los Hooks este mismo comportamiento podemos controlarlo de una mejor forma. Veamos ahora el miso ejemplo mediante el uso de Hooks.

export default MyComponent = () => {
  const [message, setMessage] = useState("Cargando mensaje...");
  const [dataState, setDataState] = setState("loading");

  if (dataState === "loading") {
    fetchToServer()
      .then((message) => {
        setDataState("loaded");
        setMessage(message);
      })
      .catch(() => {
        setDataState("error");
        setMessage("Error de conexión");
      });
  }

  return <p>{message}</p>;
};

Ambos componentes hacen exactamente lo mismo. Pero como podemos notar, el segundo tiene menos lineas. Ahora, analicemos que está pasando en el componente a detalle.

Primero, es importante hacer notar que para hacer uso de useState debemos importarlo desde React de la siguiente forma:

import { useState } from react;

Analicemos la segunda linea const [message, setMessage] = useState("Cargando mensaje…"); en la cual pasan dos cosas principalmente; se declara una constante message cuyo valor es “Cargando mensaje...” el cual asignamos mediante la función useState pasándolo como parámetro. Es importante mencionar que el valor que recibe useState no tiene porque ser una cadena y puede ser cualquiera que nos convenga según nuestro componente. Es decir yo pude pasar un true, un false, un array un numero o un objeto. Por otra parte, setMessage es una función la cual actualizará nuestro estado. En otras palabras, es equivalente a this.setState({message});.

Es muy importante tener en cuenta que nuestro componente volverá a ejecutar el Render cada ves que cambiemos el estado y por esa razón no debemos olvidar cuando hacer la petición al servidor, en este caso cuando dataState es igual a loading. De otra forma podríamos tener problemas de rendimiento o caer en un bucle innecesario.

Hasta ahora hemos visto cómo declarar el estado con useState, como acceder a él, y como modificarlo. Pero hay veces que nos gustaría hacer mas cosas con el estado antes de actualizarlo. Explico con un ejemplo:

Suponiendo que tengo un contador en el estado, lo declaro de la siguiente forma

const [counter, setCounter] = useState(0);

Después, supongamos que queremos actualizar esté contador de dos en dos y que si pasa de 10 lo reseteamos, lo podemos hacer de la siguiente forma:

setCounter((previousState) => {
    return (previousState < 10) ? previousState + 2 : 0;
});

Como podemos ver, esto nos deja un gran margen de posibilidades, ya que nos permite modificar el estado y manipularlo.

Espero que esto sirva para entender mejor el uso de estados en React y cómo agregado comento que también funciona en React Native.

Existe una forma de mejorar nuestro componente. Hacerlo mas robusto y con mejores prácticas. Para eso podemos usar useEffect el cual explicaré en la segunda parte de esta serie de posts. En cuanto publique está segunda parte, pondré la liga aquí.

Discussion (0)