React Hooks

Introducción a Hooks

1. ¿Qué son los Hooks?

En React, los Hooks son funciones especiales que te permiten «enganchar» características de React, como el estado y el ciclo de vida, en componentes funcionales. Antes de los Hooks, estas características solo estaban disponibles en componentes de clase. Los Hooks fueron introducidos en la versión 16.8 para permitir que los componentes funcionales tengan capacidad para manejar estado y efectos secundarios.

2. useState – El Estado en Componentes Funcionales

useState es el primer Hook que exploraremos. Imagina que estás construyendo una aplicación que necesita llevar un contador. Tradicionalmente, podrías pensar en usar un componente de clase, pero con useState puedes hacerlo directamente en un componente funcional.

				
					import React, { useState } from 'react';

function Counter() {
  // Inicializa el estado con un valor de 0
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>Has hecho clic {count} veces</p>
      <button onClick={() => setCount(count + 1)}>
        Haz clic aquí
      </button>
    </div>
  );
}

				
			

Aquí, useState te da dos cosas: count es el valor actual del estado, y setCount es una función que te permite actualizar ese estado.

3. useEffect – Manejando Efectos Secundarios

El siguiente Hook esencial es useEffect. Es como un caballero que se encarga de realizar tareas fuera del flujo principal de tu componente, como cargar datos, suscribirse a servicios, o limpiar recursos, similar a cómo lo harías en los métodos de ciclo de vida en componentes de clase.

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

function UserData() {
  const [data, setData] = useState(null);

  useEffect(() => {
    fetch('https://api.example.com/user')
      .then(response => response.json())
      .then(data => setData(data));
  }, []); // El array vacío indica que esto se ejecuta solo una vez, al montar el componente.

  return (
    <div>
      {data ? <p>Nombre: {data.name}</p> : <p>Cargando...</p>}
    </div>
  );
}

				
			

En este ejemplo, useEffect se utiliza para cargar datos de un usuario apenas el componente se monta.

4. Otros Hooks Importantes

  • useContext: Facilita el consumo de datos de contextos globales sin tener que anidar componentes.
  • useReducer: Para estados más complejos que pueden ser manejados con un reducer, similar a cómo se maneja el estado en Redux.
  • useCallback: Ayuda a memorizar funciones para que no se vuelvan a crear en cada renderizado.
  • useMemo: Memoriza valores calculados basados en dependencias.

Hooks personalizados

Imagina que eres un chef en tu propia cocina de código, y hasta ahora, has estado usando ingredientes estándar que React te ofrece. Pero, ¿qué sucede cuando quieres algo específico que no viene en el paquete básico? Aquí es donde los Hooks personalizados entran en juego, permitiéndote crear tus propios ingredientes especiales para hacer que tus aplicaciones sean aún más deliciosas.

¿Qué es un Hook Personalizado?

Un Hook personalizado es básicamente una función que tú creas para reutilizar lógica de estado o efectos entre múltiples componentes. React no ofrece una API específica para crearlos; simplemente usas los Hooks existentes de React de una manera que encapsule la lógica que quieres reutilizar.

Ejemplo de un Hook Personalizado

Supongamos que tienes varios componentes en tu aplicación que necesitan acceder a la ubicación del usuario. En lugar de duplicar la lógica en cada componente, puedes crear un Hook personalizado llamado useLocation.

				
					import { useState, useEffect } from 'react';

function useLocation() {
  const [location, setLocation] = useState({ latitude: null, longitude: null });
  const [error, setError] = useState(null);

  useEffect(() => {
    if (!navigator.geolocation) {
      setError('Geolocalización no soportada por este navegador.');
      return;
    }

    function onSuccess(position) {
      setLocation({
        latitude: position.coords.latitude,
        longitude: position.coords.longitude
      });
    }

    function onError(error) {
      setError(error.message);
    }

    navigator.geolocation.getCurrentPosition(onSuccess, onError);
  }, []);

  return [location, error];
}

				
			

Este Hook personalizado usa useState para manejar el estado de la ubicación y un posible error, y useEffect para ejecutar la obtención de la ubicación cuando el componente se monta. Ahora, cualquier componente que necesite la ubicación del usuario puede usar useLocation.

Uso de un Hook Personalizado

				
					import React from 'react';
import useLocation from './useLocation'; // Asumiendo que está en un archivo llamado useLocation.js

function App() {
  const [location, error] = useLocation();

  return (
    <div>
      {error ? (
        <p>Error: {error}</p>
      ) : (
        <p>Ubicación: Latitud {location.latitude}, Longitud {location.longitude}</p>
      )}
    </div>
  );
}

				
			

Beneficios de los Hooks Personalizados

  1. Reutilización de Código: Evitas la duplicación de código y mantienes tu base de código DRY (Don’t Repeat Yourself).
  2. Separación de Concerns: Cada Hook puede enfocarse en una sola tarea o funcionalidad.
  3. Composición sobre Herencia: Facilita compartir lógica sin tener que recurrir a patrones complicados de herencia o componentes de alto orden.

Contexto y manejo del estado global

Ahora que ya hemos visto los Hooks básicos y cómo crear Hooks personalizados, vamos a profundizar en una herramienta muy útil para manejar el estado global en aplicaciones React: el Contexto con el Hook useContext.

Introducción a Contexto

Imagina que tienes una gran casa con muchas habitaciones (componentes) y quieres que todos en la casa sepan si la puerta principal está cerrada o abierta (un dato global). Podrías ir de habitación en habitación diciéndolo, o simplemente podrías poner un gran cartel en la entrada que todos puedan ver. Eso es básicamente lo que hace el Contexto en React: proporciona una forma de compartir valores entre componentes sin tener que pasar explícitamente las props a través de cada nivel del árbol de componentes.

Creación de un Contexto

Primero, debes crear un Contexto que será el «cartel» en nuestra analogía. Esto se hace con React.createContext().

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

// Crea un Contexto para el estado de la puerta
const DoorContext = createContext();

// Componente proveedor que establece el valor del contexto
function DoorProvider({ children }) {
  const [isOpen, setIsOpen] = useState(false); // Estado inicial de la puerta: cerrada

  const toggleDoor = () => {
    setIsOpen(!isOpen); // Cambia el estado de la puerta
  };

  // El valor que se pasa a todos los consumidores del contexto
  return (
    <DoorContext.Provider value={{ isOpen, toggleDoor }}>
      {children}
    </DoorContext.Provider>
  );
}

				
			

Uso de useContext

Ahora, cualquier componente que necesite acceder al estado de la puerta o cambiarlo, puede usar el Hook useContext para «leer el cartel».

				
					function DoorStatus() {
  const { isOpen } = useContext(DoorContext); // Usar useContext para acceder al contexto

  return <p>La puerta está {isOpen ? 'abierta' : 'cerrada'}.</p>;
}

function DoorToggle() {
  const { toggleDoor } = useContext(DoorContext); // Acceder a la función para cambiar el estado

  return <button onClick={toggleDoor}>Cambiar estado de la puerta</button>;
}

				
			

Implementación en la Aplicación

Finalmente, asegúrate de envolver tus componentes en el DoorProvider para que puedan acceder al contexto creado.

				
					function App() {
  return (
    <DoorProvider>
      <div>
        <h1>Control de la puerta principal</h1>
        <DoorStatus />
        <DoorToggle />
      </div>
    </DoorProvider>
  );
}

				
			

Ventajas del Uso de Contexto

  1. Simplicidad: Elimina la necesidad de «prop drilling» (pasar props a través de muchos niveles de componentes).
  2. Eficiencia: Actualiza solo los componentes que dependen del contexto cuando el contexto cambia.
  3. Escalabilidad: Facilita el manejo del estado global en aplicaciones grandes, manteniendo el código limpio y organizado.