Volver a la página principal
lunes 12 agosto 2024
13

Cómo crear hooks personalizados en React

React introduce el concepto de Hooks en la versión 16.8, permitiendo a los desarrolladores usar el estado y otras características de React en componentes funcionales. Los hooks personalizados son funciones de JavaScript que utilizan uno o más hooks de React estándar para compartir lógica entre componentes sin necesidad de usar clases. En este artículo, aprenderás cómo crear y utilizar hooks personalizados en tus aplicaciones React.

¿Qué son los Hooks Personalizados?

Un hook personalizado es una función en React que permite encapsular y reutilizar la lógica de estado o efectos en componentes funcionales. Al crear un hook personalizado, puedes extraer la lógica de un componente, lo que facilita su reutilización en otros componentes sin duplicar código.

Ventajas de los Hooks Personalizados

  • Reutilización de Lógica: Encapsulan lógica reutilizable para reducir la duplicación de código.
  • Organización del Código: Facilitan la separación de la lógica y la UI, manteniendo los componentes limpios y fáciles de mantener.
  • Composición: Pueden combinarse con otros hooks personalizados o estándar para construir funcionalidades más complejas.

Paso 1: Crear un Hook Personalizado Básico

Vamos a empezar creando un hook personalizado sencillo que maneje el estado de un contador. Este ejemplo te mostrará cómo encapsular la lógica de estado en un hook reutilizable.

1.1. Definir el Hook

Primero, creamos un archivo useContador.js en el que definiremos nuestro hook personalizado:

import { useState } from 'react';

function useContador(inicial = 0) {
  const [count, setCount] = useState(inicial);

  const incrementar = () => setCount(count + 1);
  const decrementar = () => setCount(count - 1);
  const reiniciar = () => setCount(inicial);

  return { count, incrementar, decrementar, reiniciar };
}

export default useContador;

En este ejemplo:

  • useContador es nuestro hook personalizado que acepta un valor inicial (inicial) y devuelve el estado del contador (count) junto con funciones para incrementar, decrementar y reiniciar el contador.
  • El hook utiliza useState, que es un hook estándar de React, para manejar el estado interno del contador.

1.2. Usar el Hook en un Componente

Ahora que hemos creado el hook useContador, vamos a usarlo en un componente React:

import React from 'react';
import useContador from './useContador';

function Contador() {
  const { count, incrementar, decrementar, reiniciar } = useContador(10);

  return (
    <div>
      <h1>Contador: {count}</h1>
      <button onClick={incrementar}>Incrementar</button>
      <button onClick={decrementar}>Decrementar</button>
      <button onClick={reiniciar}>Reiniciar</button>
    </div>
  );
}

export default Contador;

En este componente:

  • useContador(10) inicializa el contador con un valor de 10.
  • Se accede al estado count y a las funciones incrementar, decrementar y reiniciar desde el hook, que luego se utilizan en el renderizado y en los manejadores de eventos.

Paso 2: Crear un Hook Personalizado con useEffect

Los hooks personalizados también pueden encapsular efectos secundarios mediante useEffect. Vamos a crear un hook que rastree el ancho de la ventana del navegador.

2.1. Definir el Hook con useEffect

Creamos un nuevo archivo useAnchoVentana.js para definir nuestro hook personalizado:

import { useState, useEffect } from 'react';

function useAnchoVentana() {
  const [width, setWidth] = useState(window.innerWidth);

  useEffect(() => {
    const handleResize = () => setWidth(window.innerWidth);
    window.addEventListener('resize', handleResize);

    return () => {
      window.removeEventListener('resize', handleResize);
    };
  }, []);

  return width;
}

export default useAnchoVentana;

En este hook:

  • useState se utiliza para almacenar el ancho actual de la ventana.
  • useEffect registra un manejador de eventos para actualizar el ancho cada vez que la ventana se redimensiona. También limpia el evento cuando el componente se desmonta para evitar fugas de memoria.

2.2. Usar el Hook en un Componente

Ahora podemos usar useAnchoVentana en un componente:

import React from 'react';
import useAnchoVentana from './useAnchoVentana';

function MostrarAncho() {
  const width = useAnchoVentana();

  return <h1>Ancho de la ventana: {width}px</h1>;
}

export default MostrarAncho;

Este componente muestra el ancho actual de la ventana del navegador, que se actualiza dinámicamente gracias a nuestro hook personalizado.

Paso 3: Crear un Hook Personalizado con useRef y useMemo

Los hooks personalizados también pueden utilizar otros hooks como useRef y useMemo. A continuación, crearemos un hook que maneje un temporizador simple.

3.1. Definir el Hook con useRef y useMemo

Crea un archivo useTemporizador.js para el siguiente hook:

import { useRef, useState, useEffect } from 'react';

function useTemporizador(inicial = 0) {
  const [tiempo, setTiempo] = useState(inicial);
  const intervaloRef = useRef(null);

  const iniciar = () => {
    if (!intervaloRef.current) {
      intervaloRef.current = setInterval(() => {
        setTiempo((prev) => prev + 1);
      }, 1000);
    }
  };

  const detener = () => {
    if (intervaloRef.current) {
      clearInterval(intervaloRef.current);
      intervaloRef.current = null;
    }
  };

  const reiniciar = () => {
    detener();
    setTiempo(inicial);
  };

  useEffect(() => {
    return () => detener(); // Limpia el intervalo al desmontar
  }, []);

  return { tiempo, iniciar, detener, reiniciar };
}

export default useTemporizador;

En este ejemplo:

  • useRef se utiliza para almacenar una referencia mutable al intervalo de tiempo, permitiendo su gestión entre renders.
  • useEffect asegura que el intervalo se limpia cuando el componente se desmonta.

3.2. Usar el Hook en un Componente

Finalmente, vamos a utilizar useTemporizador en un componente:

import React from 'react';
import useTemporizador from './useTemporizador';

function Temporizador() {
  const { tiempo, iniciar, detener, reiniciar } = useTemporizador(0);

  return (
    <div>
      <h1>Tiempo: {tiempo}s</h1>
      <button onClick={iniciar}>Iniciar</button>
      <button onClick={detener}>Detener</button>
      <button onClick={reiniciar}>Reiniciar</button>
    </div>
  );
}

export default Temporizador;

Este componente implementa un temporizador simple que puede iniciar, detener y reiniciar usando el hook personalizado.

Paso 4: Composición de Hooks Personalizados

Los hooks personalizados pueden componerse entre sí para crear funcionalidades más complejas. Supongamos que queremos combinar useContador y useTemporizador.

4.1. Componer Hooks

Puedes crear un nuevo hook que combine los anteriores:

import useContador from './useContador';
import useTemporizador from './useTemporizador';

function useContadorTemporizado() {
  const { count, incrementar, decrementar } = useContador();
  const { tiempo, iniciar, detener, reiniciar } = useTemporizador();

  return { count, incrementar, decrementar, tiempo, iniciar, detener, reiniciar };
}

export default useContadorTemporizado;

4.2. Usar el Hook Compuesto

Ahora puedes usar este hook compuesto en un componente:

import React from 'react';
import useContadorTemporizado from './useContadorTemporizado';

function ContadorTemporizado() {
  const { count, incrementar, decrementar, tiempo, iniciar, detener, reiniciar } = useContadorTemporizado();

  return (
    <div>
      <h1>Contador: {count}</h1>
      <button onClick={incrementar}>Incrementar</button>
      <button onClick={decrementar}>Decrementar</button>
      <h2>Tiempo: {tiempo}s</h2>
      <button onClick={iniciar}>Iniciar Temporizador</button>
      <button onClick={detener}>Detener Temporizador</button>
      <button onClick={reiniciar}>Reiniciar Temporizador</button>
    </div>
  );
}

export default ContadorTemporizado;

Este componente ahora tiene la funcionalidad combinada de un contador y un temporizador, todo gestionado por un solo hook personalizado.

Conclusión

Los hooks personalizados en React son una herramienta poderosa para encapsular y reutilizar la lógica de estado, efectos, y más. Te permiten crear componentes más limpios, organizados y fáciles de mantener.

Compartir:
Creado por:
Author photo

Jorge García

Fullstack developer