Hooks personalizados en React

Hooks personalizados en React

En React es posible escribir nuestros propios hooks personalizados, mas allá de los que vienen en la propia librería como useState(), useEffect(), etc.

¿Para que sirve un hook personalizado de React?

Cuando en JavaScript tenemos dos (o más) funciones que tienen código en común, si queremos compartirlo en lugar de tenerlo duplicado en ambas funciones, lo habitual es extraer dicho código, colocarlo en una tercera función, y llamar a dicha función desde las dos funciones originales.

Los hooks personalizados tienen la misma finalidad. Sirven para extraer funcionalidad de dos (o más) componentes que tienen código repetido (o muy similar).

En JavaScript, también puede surgirnos la necesidad de simplificar una función excesivamente extensa. Para ello lo que solemos  hacer es extraer parte de su funcionalidad en otra función, aunque no tengamos intención de usar esa nueva función en otra parte.

Los hooks personalizados también pueden servir para esto mismo, es decir, para simplificar un componente, extrayendo parte de su código y aislándolo en un hook personalizado.

¿Como hacer un hook personalizado en React?

Un hook personalizado es simplemente una función cuyo nombre (por simple convención) empieza por «use», y que puede (y suele) llamar a otros hooks (e.g. useState(), useEffect(), otros hooks personalizados…).

Y ya está, nada más.

No se trata de una funcionalidad especial de React, ya que son funciones normales y corrientes, con un nombre y un uso que son simplemente producto de una convención.

NOTA: Es importante tener en cuenta que si un hook personalizado hace uso de useState(), y dos componentes diferentes hacen uso de dicho hook personalizado, esto no implica que estén compartiendo el mismo estado. En estos casos, cada llamada al hook personalizado obtiene su propio estado.

Ejemplo de un hook personalizado en React

Simplemente con la descripción anterior de lo que es un hook ya deberías de poder implementarlo sin problemas, pero mejor si vemos un ejemplo.

Imaginemos que tenemos dos componentes diferentes, uno para mostrar la información de una Pelicula, y otro para mostrar la información de un Libro. Ambos componentes, al ser montados, realizan una conexión al servidor para leer la información necesaria desde un archivo .json

Si pudiéramos aislar la funcionalidad de pedir el archivo al servidor en un hook personalizado, podríamos tener unos componentes más sencillos y además evitaríamos repetir código.

A continuación tenéis este ejemplo con el código del hook personalizado y el código de los componentes haciendo uso de dicho hook.

Hook Personalizado

import { useState, useEffect } from "react";

export function usePedirJsonAlServidor(url) {
  // estamos cargando la información?
  const [cargando, setCargando] = useState(false);
  // los datos del JSON los guardaremos aqui
  const [datos, setDatos] = useState({});
  // si se produce algun error lo guardaremos aqui
  const [error, setError] = useState("");

  useEffect(() => {

    (async()=>{
      try {
        setCargando(true);
        let response = await fetch(url);
        if (response.ok) {        
            let d = await response.json(); // procesamos json        
            setDatos(d);              
        } else throw new Error("No se puede acceder a la URL: " + url);
      } catch (e) {
        setError(e.message);
      } finally {
        setCargando(false);
      }
    })();   

  }, []);

  // devolvemos cargando + datos + error
  return { cargando, datos, error }
}

Componente Pelicula

import { usePedirJsonAlServidor } from './usePedirJsonAlServidor'

export default function Pelicula() {
  // obviamente, la URL podria ser una propiedad del componente...
  const { cargando, datos, error } = usePedirJsonAlServidor("/pelicula01.json");

  if (error) {
    return (
      <div className="pelicula">
        <p>ERROR: {error}</p>
      </div>
    )
  }

  return (
    <div className="pelicula">
      <p>Título: {cargando ? "..." : datos.titulo}</p>
      <p>Año: {cargando ? "..." : datos.anyo}</p>
      <p>Director: {cargando ? "..." : datos.director}</p>
    </div>
  )
}

Componente Libro

import { usePedirJsonAlServidor } from './usePedirJsonAlServidor'

export default function Libro() {
  // obviamente, la URL podria ser una propiedad del componente...
  const { cargando, datos, error } = usePedirJsonAlServidor("/libro01.json");

  if (error) {
    return (
      <div className="libro">
        <p>ERROR: {error}</p>
      </div>
    )
  }

  return (
    <div className="libro">
      <p>Título: {cargando ? "..." : datos.titulo}</p>
      <p>Tipo: {cargando ? "..." : datos.tipo}</p>
      <p>Autor: {cargando ? "..." : datos.autor}</p>
    </div>
  )
}

Como se puede ver en este ejemplo, el haber aislado la funcionalidad compartida por los dos componentes en un único hook personalizado que se encarga de realizar la petición de información al servidor, nos ha evitado repetir código y a su vez, nos ha permitido escribir unos componentes más sencillos.

NOTA: Si tienes problemas de CORS al realizar las peticiones fetch, puedes consultar este otro artículo.

Conclusión

Tal y como se puede ver, los hooks personalizados de React son un concepto muy sencillo y que nos va a permitir escribir un código más limpio y reutilizable.

Recuerda que los hooks personalizados son funciones normales y corrientes, y que lo que las convierte en hooks es una simple convención.

Para más información puedes consultar la página oficial.

Escribe un comentario