Una de las características más importantes introducidas en React 16.8 son los Hooks, que permiten a los desarrolladores usar el estado y otras funcionalidades de React en componentes funcionales, sin necesidad de escribir clases. Esto simplifica el código y facilita la reutilización de la lógica.
¿Qué son los Hooks?
Los Hooks son funciones especiales que te permiten incorporar características de React, como el estado y los efectos secundarios, en componentes funcionales. Antes de los Hooks, estas características solo estaban disponibles en componentes de clase.
Reglas básicas de los Hooks
-
Llamar a los Hooks en el nivel superior: No debes llamar a los Hooks dentro de bucles, condiciones o funciones anidadas. Esto asegura que los Hooks se ejecuten en el mismo orden en cada renderizado.
-
Usar Hooks solo en componentes funcionales: Los Hooks no deben ser llamados desde funciones JavaScript normales. Solo deben usarse dentro de componentes funcionales o en otros Hooks personalizados.
Hooks más comunes
A continuación, te presentamos los Hooks más utilizados y cómo se usan:
1. useState
useState
es el Hook más básico y te permite agregar estado a un componente funcional. Devuelve un array con dos elementos: el valor actual del estado y una función para actualizarlo.
import React, { useState } from 'react'; function Contador() { const [cuenta, setCuenta] = useState(0); return ( <div> <p>Has hecho clic {cuenta} veces</p> <button onClick={() => setCuenta(cuenta + 1)}> Incrementar </button> </div> ); }
En este ejemplo, cuenta
es el valor del estado, y setCuenta
es la función que lo actualiza. Cada vez que se hace clic en el botón, el valor de cuenta
aumenta en 1.
2. useEffect
useEffect
te permite manejar efectos secundarios, como llamadas a APIs, suscripciones o manipulación del DOM. Se ejecuta después de que el componente se renderiza.
import React, { useState, useEffect } from 'react'; function Ejemplo() { const [contador, setContador] = useState(0); useEffect(() => { document.title = `Has hecho clic ${contador} veces`; }, [contador]); // Solo se ejecuta cuando 'contador' cambia return ( <div> <p>Has hecho clic {contador} veces</p> <button onClick={() => setContador(contador + 1)}> Haz clic </button> </div> ); }
En este caso, useEffect
actualiza el título de la página cada vez que el valor de contador
cambia. El segundo argumento de useEffect
es un array de dependencias que indica cuándo debe ejecutarse el efecto.
3. useContext
useContext
te permite acceder al contexto de React, que es una forma de compartir datos entre componentes sin necesidad de pasar props manualmente en cada nivel.
import React, { useContext } from 'react'; const TemaContexto = React.createContext(); function Boton() { const tema = useContext(TemaContexto); return ( <button style={{ background: tema.fondo, color: tema.texto }}> Botón con tema </button> ); } function App() { const tema = { fondo: 'black', texto: 'white' }; return ( <TemaContexto.Provider value={tema}> <Boton /> </TemaContexto.Provider> ); }
Aquí, useContext
permite que el componente Boton
acceda al valor del contexto TemaContexto
sin necesidad de pasarlo como prop.
4. useReducer
useReducer
es similar a useState
, pero está diseñado para manejar estados más complejos. Es especialmente útil cuando el estado depende de acciones anteriores o cuando la lógica de actualización es complicada.
import React, { useReducer } from 'react'; function reducer(state, action) { switch (action.type) { case 'incrementar': return { cuenta: state.cuenta + 1 }; case 'decrementar': return { cuenta: state.cuenta - 1 }; default: throw new Error('Acción no válida'); } } function Contador() { const [state, dispatch] = useReducer(reducer, { cuenta: 0 }); return ( <div> <p>Cuenta: {state.cuenta}</p> <button onClick={() => dispatch({ type: 'incrementar' })}>+</button> <button onClick={() => dispatch({ type: 'decrementar' })}>-</button> </div> ); }
En este ejemplo, useReducer
maneja un estado que puede incrementarse o decrementarse mediante acciones (incrementar
y decrementar
).
5. useRef
useRef
te permite crear una referencia mutable que persiste durante todo el ciclo de vida del componente. Es útil para acceder a elementos del DOM o almacenar valores que no necesitan provocar un nuevo renderizado cuando cambian.
import React, { useRef } from 'react'; function InputFocus() { const inputRef = useRef(null); const handleClick = () => { inputRef.current.focus(); }; return ( <div> <input ref={inputRef} type="text" /> <button onClick={handleClick}>Enfocar input</button> </div> ); }
Aquí, useRef
se usa para enfocar un input cuando se hace clic en el botón.
6. useMemo
useMemo
te permite memorizar un valor calculado, evitando recalcularlo en cada renderizado si sus dependencias no han cambiado. Es útil para optimizar el rendimiento.
import React, { useMemo, useState } from 'react'; function CalculoCostoso({ numero }) { const resultado = useMemo(() => { console.log('Calculando...'); return numero * 2; }, [numero]); return <p>Resultado: {resultado}</p>; } function App() { const [numero, setNumero] = useState(1); return ( <div> <CalculoCostoso numero={numero} /> <button onClick={() => setNumero(numero + 1)}>Incrementar</button> </div> ); }
En este ejemplo, el cálculo solo se realiza cuando numero
cambia, evitando recalcular en cada renderizado.
7. useCallback
useCallback
es similar a useMemo
, pero en lugar de memorizar un valor, memoriza una función. Esto es útil cuando pasas funciones como props a componentes hijos y quieres evitar recrearlas en cada renderizado.
import React, { useState, useCallback } from 'react'; function Hijo({ onClick }) { console.log('Hijo renderizado'); return <button onClick={onClick}>Haz clic</button>; } function App() { const [contador, setContador] = useState(0); const handleClick = useCallback(() => { setContador(contador + 1); }, [contador]); return ( <div> <p>Contador: {contador}</p> <Hijo onClick={handleClick} /> </div> ); }
Aquí, useCallback
asegura que la función handleClick
no se recrea en cada renderizado, a menos que contador
cambie.
8. useLayoutEffect
useLayoutEffect
es similar a useEffect
, pero se ejecuta sincrónicamente después de que React actualiza el DOM, pero antes de que el navegador pinte la pantalla. Es útil para medir el layout o realizar cambios en el DOM antes de que el usuario vea la actualización.
import React, { useLayoutEffect, useRef, useState } from 'react'; function MedirElemento() { const [ancho, setAncho] = useState(0); const ref = useRef(null); useLayoutEffect(() => { setAncho(ref.current.offsetWidth); }, []); return ( <div> <p ref={ref}>Este texto tiene un ancho de {ancho}px</p> </div> ); }
En este caso, useLayoutEffect
mide el ancho del elemento antes de que se pinte en la pantalla.
9. useTransition
useTransition
te permite marcar ciertas actualizaciones de estado como no bloqueantes, lo que mejora el rendimiento en aplicaciones con muchas actualizaciones.
import React, { useState, useTransition } from 'react'; function Lista() { const [busqueda, setBusqueda] = useState(''); const [resultados, setResultados] = useState([]); const [isPending, startTransition] = useTransition(); const handleChange = (e) => { const valor = e.target.value; setBusqueda(valor); startTransition(() => { const resultadosFiltrados = Array.from({ length: 10000 }, (_, i) => `Resultado ${i + 1}`).filter( (item) => item.includes(valor) ); setResultados(resultadosFiltrados); }); }; return ( <div> <input type="text" value={busqueda} onChange={handleChange} /> {isPending ? <p>Cargando...</p> : resultados.map((item) => <p key={item}>{item}</p>)} </div> ); }
Aquí, useTransition
permite que la interfaz de usuario siga siendo responsiva mientras se filtran los resultados.
10. useDebugValue
useDebugValue
es un Hook útil para depurar Hooks personalizados. Te permite mostrar una etiqueta en las herramientas de desarrollo de React.
import React, { useState, useDebugValue } from 'react'; function useContador() { const [contador, setContador] = useState(0); useDebugValue(`Contador: ${contador}`); return [contador, setContador]; } function App() { const [contador, setContador] = useContador(); return ( <div> <p>Contador: {contador}</p> <button onClick={() => setContador(contador + 1)}>Incrementar</button> </div> ); }
En este ejemplo, useDebugValue
muestra el valor actual del contador en las herramientas de desarrollo.
De este modo, los Hooks han revolucionado la forma en que se desarrollan componentes en React, permitiendo manejar el estado y otras funcionalidades sin necesidad de recurrir a clases. Con opciones como useState
, useEffect
, useContext
y otros más avanzados como useReducer
o useTransition
, los desarrolladores pueden escribir código más limpio, reutilizable y optimizado.