Cómo crear un hook personalizado

Cómo crear un hook personalizado

Todo programador busca escribir un código que sea reutilizable de la mejor manera. Sobre el desarrollo web, más específicamente en React, los Hooks nos ofrecen la opción de alterar estados y ciclo de vida a partir de componentes funcionales.

En este artículo, vamos a hablar un poco sobre qué son los hooks y cómo crear uno personalizado para tu proyecto.

Hooks

¿Qué son?

Implementados en la versión 16.8.0, los Hooks permitieron utilizar diversos recursos por medio de funciones de una forma simple, dejando de lado el uso de clases para acceder a funcionalidades de React.

Los Hooks permiten reutilizar una misma funcionalidad varias veces en la aplicación. De esta forma, proporcionan una manera más simple y concisa de lidiar con el estado y el ciclo de vida en componentes funcionales, haciendo el código más legible y más fácil de mantener.

Hooks más utilizados

useState

Se usa para agregar estado a componentes funcionales, es decir, permite agregar una variable de estado a tu componente. Retorna un par de valores: el estado actual y una función para actualizarlo. Ejemplo:

import { useState } from 'react';

export default function Main(){
const [state, setState] = useState(0);

return (
  <div>
  <p>{state}</p>
  <button onClick={() => setState(state + 1)}>+</button>
  </div>
);
}

useEffect

El useEffect permite ejecutar efectos secundarios en el código. Se usa para manejar tareas asíncronas, integración con APIs externas, manipulación de eventos, entre otros. Ejemplo:

import { useState, useEffect } from 'react';

export default function Main(){
const [state, setState] = useState(0);

useEffect(() => {
  document.title = `Você clicou ${count} vezes`;
});

return (
  <div>
  <p>Você clicou {count} vezes</p>
  <button onClick={() => setCount(count + 1)}>
    Clique aqui
  </button>
  </div>
);
};

En este ejemplo, el título del documento se convierte en un mensaje personalizado, informando el número de clics del botón.

useRef

Este hook permite crear una referencia mutable que persiste entre renderizaciones. Puede usarse para acceder al DOM directamente o para almacenar un valor inmutable durante el ciclo de vida del componente. En el ejemplo a continuación, creamos la referencia al elemento del DOM utilizando useRef. Esta referencia se utiliza para enfocar el elemento cuando se hace clic en el botón.

import { useRef } from 'react';

export default function Home() {
const element = useRef(null);

const handleButton = () => {
  element.current.focus();
};

return (
  <div className='container'>
  <form>
    <input type="text" ref={element} />
    <button onClick={handleButton}>Clicar</button>
  </form>
  </div>
);
};

useContext

Este hook permite que accedas al contexto de un componente, con el objetivo de compartir datos globalmente, sin necesidad de pasar props manualmente a través de cada nivel. Ve el ejemplo a continuación:

import { createContext, useContext, useState } from 'react';

const CountContext = createContext();

function CountProvider({ children }) {
  const [count, setCount] = useState(0);

  return (
    <CountContext.Provider value={{ count, setCount }}>
      {children}
    </CountContext.Provider>
  );
}

export { CountProvider, CountContext };

import { useContext } from 'react';
import { CountContext } from './CountContext';

function handleCount() {
  const { count } = useContext(CountContext);

  return <p>Contador: {count}</p>;
}

function handleButton() {
  const { count, setCount } = useContext(CountContext);

  const increment = () => {
    setCount(prevCount => prevCount + 1);
  };

  return <button onClick={increment}>Incrementar</button>;
}

import { CountProvider } from './CountContext';
import handleCount from './handleCount';
import handleButton from './handleButton';

export default function App() {
  return (
    <CountProvider>
      <div>
        <handleCount />
        <handleButton />
      </div>
    </CountProvider>
  );
};

Primero, se crea el contexto mediante la función createContext. Tendrá el estado que será compartido entre componentes. Luego, el contexto se importa en los componentes que necesitan acceder al estado global. Finalmente, en la función App, los componentes (handleCount y handleButton) son envueltos por el proveedor creado para compartir el estado global.

El estado count puede ser compartido entre los componentes handleCount y handleButton, evitando la necesidad de pasar props manualmente entre los componentes padres e hijos.

useCallback

Utilizado para evitar la creación de nuevas funciones en cada renderización, especialmente cuando esas funciones se pasan como props a componentes hijos, optimizando el rendimiento.

import { useState, useCallback } from 'react';

export default function FilteredList() {
  const [items, setItems] = useState([
    'Maçã', 'Banana', 'Laranja', 'Morango', 'Uva'
  ]);

  const [filterText, setFilterText] = useState('');

  const handleFilterChange = useCallback((event) => {
    setFilterText(event.target.value);
  }, []);

  const filteredItems = items.filter(item =>
    item.toLowerCase().includes(filterText.toLowerCase())
  );

  return (
    <div>
      <input
        type="text"
        placeholder="Filtrar itens..."
        value={filterText}
        onChange={handleFilterChange}
      />
      <ul>
        {filteredItems.map((item, index) => (
          <li key={index}>{item}</li>
        ))}
      </ul>
    </div>
  );
};

En este ejemplo, el useCallback se usa para memorizar la función handleFilterChange, que es responsable de actualizar el estado filterText basado en el texto ingresado por el usuario. Sin el useCallback, la función handleFilterChange se recrearía en cada renderización del componente FilteredList, lo que podría llevar a re-renderizaciones innecesarias del componente.

¿Por qué hook personalizado?

El uso de hooks personalizados en React ofrece diversas ventajas para la aplicación. La reutilización de lógica es uno de los principales puntos, ya que la creación de hooks personalizados permite encapsular una lógica compleja en un único lugar. Esto facilita la reutilización de esa lógica en varios componentes, evitando la duplicación de código.

Cuando encapsulamos detalles complejos de implementación en los hooks personalizados, logramos dejar una interfaz más simple y clara para los componentes que los utilizan.

Además, cuando sea necesario modificar el código del hook, solo un único lugar necesitará cambios. Esto facilita el mantenimiento y reduce la posibilidad de introducir errores al actualizar varias partes del código.

¡Vamos a la práctica!

  1. Configuraciones iniciales del proyecto

Vamos a iniciar el proyecto desde cero. Crea una carpeta donde será almacenado. Aquí la nombraremos como custom-hook. En tu terminal, ejecuta el siguiente código:

npx create-react-app custom-hook

Luego:

cd custom-hook

npm start

Una nueva ventana se abrirá en tu navegador, como se muestra en la figura a continuación.


Ahora, haremos la organización de nuestras carpetas y archivos. En este punto, debes tener la siguiente estructura de carpetas:

Podemos eliminar los siguientes archivos:

  • En la carpeta public:
  • logo192.png e logo512.png;
  • manifest.json;
  • robots.txt.
  • En la carpeta src borraremos todos, excepto el index.js e App.js;

Algunos ajustes adicionales deben realizarse:

  • En la carpeta public, en index.html: elimina los comentarios y referencias de las imágenes que borramos.
  • En la carpeta src:
  • Crea una nueva carpeta llamada pages;
  • En la carpeta pages, agrega un nuevo archivo Home.js con el siguiente fragmento:
  • Crea dos archivos más en la carpeta pages, denominados About.js y Contact.js. Tendrán el siguiente contenido:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <meta name="theme-color" content="#000000" />
  <meta
  name="description"
  content="Web site created using create-react-app"
  />
  <title>React App</title>
</head>
<body>
  <noscript>You need to enable JavaScript to run this app.</noscript>
  <div id="root"></div>
</body>
</html>

export default function Home() {
return (
  <div>
      <h1>Home</h1>
  </div>
);
};

About.js

export default function About() {
return (
  <div>
  <h1>About</h1>
  </div>
);
};

Contac.js

export default function Contact() {
return (
  <div>
  <h1>Contact</h1>
  </div>
);
};

  • En el archivo App.js, que está en src, deja solo el siguiente código:
  • Y por último, modifica el archivo index.js:

export default function App() {
return (
  <div>
  <h1>Custom Hooks</h1>
  </div>
);
};

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
  <App />
</React.StrictMode>
);

Con esto, ya no tendremos ningún error en el terminal:

Tus carpetas y archivos tendrán esta estructura ahora:

Y tu página tendrá esta apariencia inicial:

2. Ruteo

Necesitaremos instalar el react-router-dom, que será la biblioteca responsable del ruteo de nuestras páginas. En el terminal, ejecuta:

npm i react-router-dom

Con el react-router-dom instalado, vamos a importarlo en nuestro App.js, junto con nuestras pages. Las importaciones serán así::

import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import { About } from './pages/About';
import { Contact } from './pages/Contact';
import { Home } from './pages/Home';

El BrowserRouter es el responsable del ruteo, rodeando las rutas del proyecto. Por convención, se renombra como Router. El Route son las rutas propiamente dichas, y el Routes es quien describe las rutas, rodeándolas.

Ahora, añadiremos las rutas de cada página:

import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import About from './pages/About';
import Contact from './pages/Contact';
import Home from './pages/Home';

export default function App() {
return (
  <Router>
  <Routes>
    <Route path='/' element={<Home />} />
    <Route path='/about' element={<About />} />
    <Route path='/contact' element={<Contact />} />
  </Routes>
  </Router>
);
};

El Route presenta dos parámetros: el path y el element. El path es la ubicación que los usuarios utilizarán para acceder a esa página. En el element, estará el componente que se renderizará para esa ruta, en este caso serán Home, About y Contact.

3. Desarrollando el Hook

Dentro de src, crea una carpeta llamada hooks. Dentro de esta nueva carpeta, vamos crear un archivo llamado usePath.js, en el cual desarrollaremos nuestro hook personalizado.

La idea de este hook es verificar en qué página se encuentra el usuario y obtener su path. Para obtener este parámetro, utilizaremos useLocation, que es un hook nativo de React. El usePath quedará de la siguiente manera:

import { useLocation } from "react-router";

export default function usePath() {
    let isHome = false;
    const { pathname } = useLocation();

    if (pathname === '/') {
        isHome = true;
    };

    return {
        isHome
    };
};

Desestructuraremos el useLocation para usar solo el pathname. Utilizaremos la variable isHome como una bandera. Si el usuario está en la página Home, la variable isHome será true. De lo contrario, será false.

Así, solo hace falta importar nuestro hook en nuestras páginas y elaborar la lógica. El código en cada página será:

Home.js

import usePath from "../hooks/usePath";

export default function Home() {
const {isHome} = usePath();

return (
  <div>
  <h1>Home</h1>
  {isHome ?
    <h3 style={{color:'green'}}>Você está na página principal</h3> :
    <h3 style={{color:'red'}}>Você não está na página principal</h3>
    }
  </div>
);
};

About.js

import usePath from "../hooks/usePath";

export default function About() {
const {isHome} = usePath();

return (
  <div>
  <h1>About</h1>
  {isHome ?
    <h3 style={{color:'green'}}>Você está na página principal</h3> :
    <h3 style={{color:'red'}}>Você não está na página principal</h3>
  }
  </div>
);
};

Contact.js

import usePath from "../hooks/usePath";

export default function Contact() {
const {isHome} = usePath();

return (
  <div>
  <h1>Contact</h1>
  {isHome ?
    <h3 style={{color:'green'}}>Você está na página principal</h3> :
    <h3 style={{color:'red'}}>Você não está na página principal</h3>
  }
  </div>
);
};

Importamos el hook usePath en cada página y lo desestructuramos para tener acceso a la variable isHome. Añadimos un ternario para la lógica de verificación de la ruta. Si está en la home, aparecerá la frase en verde 'Estás en la página principal'. Si no, la frase en rojo 'No estás en la página principal' se renderizará en la pantalla.

Nuestro resultado final:

Conclusión

En este artículo, abordamos los hooks, que son herramientas muy utilizadas en React. También vimos algunos hooks nativos de React y cómo desarrollar un hook personalizado. Crear un hook personalizado para tu aplicación ofrece varias ventajas, entre ellas, mejora la organización, reutilización y legibilidad de tu código.

💡
Las opiniones y comentarios emitidos en este artículo son propiedad única de su autor y no necesariamente representan el punto de vista de Listopro.

Listopro Community da la bienvenida a todas las razas, etnias, nacionalidades, credos, géneros, orientaciones, puntos de vista e ideologías, siempre y cuando promuevan la diversidad, la equidad, la inclusión y el crecimiento profesional de los profesionales en tecnología.