El problema: lógica repetida
Imagina dos componentes que necesitan un interruptor (encendido/apagado). En ambos escribirías lo mismo:
const [abierto, setAbierto] = useState(false);
const alternar = () => setAbierto((v) => !v);
Copiar y pegar esa lógica funciona, pero se desincroniza con el tiempo. La solución de React son los hooks personalizados.
Qué es un hook personalizado
Un hook personalizado es, simplemente, una función cuyo nombre empieza por
use y que usa otros hooks por dentro. Encapsula lógica con estado y
devuelve lo que el componente necesita:
function useToggle(inicial = false) {
const [valor, setValor] = useState(inicial);
const alternar = () => setValor((v) => !v);
return [valor, alternar];
}
Ahora cualquier componente reutiliza esa lógica sin duplicarla:
function Panel() {
const [abierto, alternar] = useToggle(false);
return (
<div>
<button onClick={alternar}>Mostrar/ocultar</button>
{abierto && <p>Contenido visible</p>}
</div>
);
}
Lo importante: cada componente que llama a useToggle obtiene su propio
estado, aislado. Compartes la lógica, no el estado.
Otro ejemplo: useContador
Un hook puede componer varios hooks y exponer la API que más convenga (un objeto, un array, funciones...):
function useContador(inicial = 0) {
const [cuenta, setCuenta] = useState(inicial);
const incrementar = () => setCuenta((c) => c + 1);
const reiniciar = () => setCuenta(inicial);
return { cuenta, incrementar, reiniciar };
}
Las reglas de los hooks
Para que React pueda asociar cada llamada con su estado, los hooks tienen dos reglas inquebrantables:
- Llámalos solo en el nivel superior. Nunca dentro de
if, bucles, condicionales o funciones anidadas. El orden de las llamadas debe ser el mismo en cada render. - Llámalos solo desde componentes de React o desde otros hooks personalizados. No desde funciones JavaScript normales.
// ❌ MAL: hook dentro de un if (el orden cambia entre renders)
if (activo) {
const [x, setX] = useState(0);
}
// ✅ BIEN: en el nivel superior; la condición va dentro
const [x, setX] = useState(0);
if (activo) { /* usa x */ }
Un hook personalizado es solo una convención de nombre (
use...) más estas reglas. No hay "magia": es una función que organiza tus hooks.
Ejemplos
Un hook useToggle reutilizable
function useToggle(inicial) {
const [valor, setValor] = React.useState(inicial);
const alternar = () => setValor((v) => !v);
return [valor, alternar];
}
// Simulación: dos "instancias" tienen estado independiente
let estado1 = false, estado2 = true;
console.log("inicio:", estado1, estado2);