16. Resumen de Hooks
📚 16.1 Tabla de referencia rápida
Section titled “📚 16.1 Tabla de referencia rápida”| Hook | Propósito | Uso común |
|---|---|---|
useState | Estado local | Formularios, toggles, contadores |
useEffect | Efectos secundarios | Fetch, suscripciones, DOM |
useContext | Consumir contexto | Tema, auth, idioma |
useRef | Referencias mutables | DOM, valores sin re-render |
useMemo | Memorizar valores | Cálculos costosos |
useCallback | Memorizar funciones | Callbacks en props |
useReducer | Estado complejo | Múltiples acciones |
useLayoutEffect | Efecto síncrono | Mediciones DOM |
useId | IDs únicos | Accesibilidad |
🔄 16.2 useState
Section titled “🔄 16.2 useState”Sintaxis
Section titled “Sintaxis”const [estado, setEstado] = useState(valorInicial);Ejemplos
Section titled “Ejemplos”import { useState } from 'react';
// Valor primitivoconst [count, setCount] = useState(0);setCount(count + 1);setCount(prev => prev + 1); // Actualización funcional
// Objetoconst [user, setUser] = useState({ name: '', email: '' });setUser({ ...user, name: 'Juan' }); // Spread para actualizar
// Arrayconst [items, setItems] = useState([]);setItems([...items, nuevoItem]); // AgregarsetItems(items.filter(i => i.id !== id)); // Eliminar
// Inicialización lazy (costosa)const [data, setData] = useState(() => {return calcularValorInicial();});⚡ 16.3 useEffect
Section titled “⚡ 16.3 useEffect”Sintaxis
Section titled “Sintaxis”useEffect(() => {// Efectoreturn () => { // Cleanup (opcional)};}, [dependencias]);Patrones comunes
Section titled “Patrones comunes”import { useEffect } from 'react';
// Ejecutar una vez al montaruseEffect(() => {console.log('Componente montado');}, []);
// Ejecutar cuando cambia una dependenciauseEffect(() => {console.log('userId cambió:', userId);}, [userId]);
// Con cleanupuseEffect(() => {const timer = setInterval(() => {}, 1000);return () => clearInterval(timer);}, []);
// Fetch de datosuseEffect(() => {const fetchData = async () => { const res = await fetch('/api/data'); const data = await res.json(); setData(data);};fetchData();}, []);🌐 16.4 useContext
Section titled “🌐 16.4 useContext”Sintaxis
Section titled “Sintaxis”// Crear contextoconst MiContexto = createContext(valorDefault);
// Proveer<MiContexto.Provider value={valor}>{children}</MiContexto.Provider>
// Consumirconst valor = useContext(MiContexto);Ejemplo completo
Section titled “Ejemplo completo”import { createContext, useContext, useState } from 'react';
// 1. Crear contextoconst TemaContext = createContext();
// 2. Providerfunction TemaProvider({ children }) {const [tema, setTema] = useState('claro');const toggle = () => setTema(t => t === 'claro' ? 'oscuro' : 'claro');
return ( <TemaContext.Provider value={{ tema, toggle }}> {children} </TemaContext.Provider>);}
// 3. Hook personalizadofunction useTema() {return useContext(TemaContext);}
// 4. Usofunction Boton() {const { tema, toggle } = useTema();return <button onClick={toggle}>Tema: {tema}</button>;}📌 16.5 useRef
Section titled “📌 16.5 useRef”Sintaxis
Section titled “Sintaxis”const ref = useRef(valorInicial);// Acceder: ref.current// Modificar: ref.current = nuevoValor (no causa re-render)Usos comunes
Section titled “Usos comunes”import { useRef, useEffect } from 'react';
// Referencia a elemento DOMfunction Input() {const inputRef = useRef(null);
const enfocar = () => { inputRef.current.focus();};
return <input ref={inputRef} />;}
// Guardar valor anteriorfunction usePrevious(value) {const ref = useRef();useEffect(() => { ref.current = value;}, [value]);return ref.current;}
// Contador de renders (sin causar re-render)function Componente() {const renderCount = useRef(0);renderCount.current++;console.log('Renders:', renderCount.current);}
// Guardar timerfunction Timer() {const timerRef = useRef(null);
const iniciar = () => { timerRef.current = setInterval(() => {}, 1000);};
const detener = () => { clearInterval(timerRef.current);};}🧮 16.6 useMemo
Section titled “🧮 16.6 useMemo”Sintaxis
Section titled “Sintaxis”const valorMemorizado = useMemo(() => {return calcularValor(dependencia);}, [dependencia]);Cuándo usar
Section titled “Cuándo usar”import { useMemo } from 'react';
// ✅ Cálculo costosofunction Lista({ items, filtro }) {const itemsFiltrados = useMemo(() => { return items.filter(item => item.nombre.toLowerCase().includes(filtro.toLowerCase()) );}, [items, filtro]);
return <ul>{itemsFiltrados.map(i => <li key={i.id}>{i.nombre}</li>)}</ul>;}
// ✅ Objeto/array como prop (evita re-renders)function Padre() {const config = useMemo(() => ({ theme: 'dark', size: 'large'}), []);
return <Hijo config={config} />;}
// ❌ NO usar para valores simplesconst doble = useMemo(() => count * 2, [count]); // Innecesario🔗 16.7 useCallback
Section titled “🔗 16.7 useCallback”Sintaxis
Section titled “Sintaxis”const funcionMemorizada = useCallback(() => {// hacer algo}, [dependencias]);Cuándo usar
Section titled “Cuándo usar”import { useCallback, memo } from 'react';
// ✅ Callback pasado a componente memorizadoconst HijoMemorizado = memo(function Hijo({ onClick }) {console.log('Hijo renderizado');return <button onClick={onClick}>Click</button>;});
function Padre() {const [count, setCount] = useState(0);
// Sin useCallback: Hijo se re-renderiza siempre// Con useCallback: Hijo solo se re-renderiza si cambian depsconst handleClick = useCallback(() => { console.log('Click!');}, []);
return ( <div> <p>{count}</p> <button onClick={() => setCount(c => c + 1)}>+</button> <HijoMemorizado onClick={handleClick} /> </div>);}
// ✅ Callback en dependencias de useEffectfunction Buscador({ query }) {const buscar = useCallback(async () => { const res = await fetch(`/api/search?q=${query}`); return res.json();}, [query]);
useEffect(() => { buscar().then(setResultados);}, [buscar]);}🎛️ 16.8 useReducer
Section titled “🎛️ 16.8 useReducer”Sintaxis
Section titled “Sintaxis”const [state, dispatch] = useReducer(reducer, initialState);
function reducer(state, action) {switch (action.type) { case 'ACTION': return { ...state, /* cambios */ }; default: return state;}}Ejemplo completo
Section titled “Ejemplo completo”import { useReducer } from 'react';
const initialState = { count: 0 };
function reducer(state, action) {switch (action.type) { case 'increment': return { count: state.count + 1 }; case 'decrement': return { count: state.count - 1 }; case 'reset': return initialState; case 'set': return { count: action.payload }; default: throw new Error('Acción desconocida');}}
function Contador() {const [state, dispatch] = useReducer(reducer, initialState);
return ( <div> <p>Count: {state.count}</p> <button onClick={() => dispatch({ type: 'increment' })}>+</button> <button onClick={() => dispatch({ type: 'decrement' })}>-</button> <button onClick={() => dispatch({ type: 'reset' })}>Reset</button> <button onClick={() => dispatch({ type: 'set', payload: 10 })}> Set 10 </button> </div>);}useState vs useReducer
Section titled “useState vs useReducer”| useState | useReducer |
|---|---|
| Estado simple | Estado complejo |
| Pocas actualizaciones | Múltiples acciones |
| Lógica simple | Lógica compartida |
🎨 16.9 Otros Hooks
Section titled “🎨 16.9 Otros Hooks”useLayoutEffect
Section titled “useLayoutEffect”import { useLayoutEffect, useRef, useState } from 'react';
// Se ejecuta ANTES de pintar en pantalla// Útil para mediciones DOMfunction Tooltip({ children }) {const ref = useRef();const [height, setHeight] = useState(0);
useLayoutEffect(() => { // Medir antes de que el usuario vea setHeight(ref.current.getBoundingClientRect().height);}, []);
return <div ref={ref}>{children}</div>;}import { useId } from 'react';
function Input({ label }) {const id = useId();
return ( <div> <label htmlFor={id}>{label}</label> <input id={id} /> </div>);}
// Genera IDs únicos como ":r1:", ":r2:", etc.🛠️ 16.10 Hooks personalizados
Section titled “🛠️ 16.10 Hooks personalizados”Patrón
Section titled “Patrón”// Nombre empieza con "use"function useCustomHook(params) {// Puede usar otros hooksconst [state, setState] = useState();
useEffect(() => { // lógica}, []);
// Retorna valores/funcionesreturn { state, setState };}Ejemplos útiles
Section titled “Ejemplos útiles”// useLocalStoragefunction useLocalStorage(key, initialValue) {const [value, setValue] = useState(() => { const saved = localStorage.getItem(key); return saved ? JSON.parse(saved) : initialValue;});
useEffect(() => { localStorage.setItem(key, JSON.stringify(value));}, [key, value]);
return [value, setValue];}
// useTogglefunction useToggle(initial = false) {const [value, setValue] = useState(initial);const toggle = useCallback(() => setValue(v => !v), []);return [value, toggle];}
// useDebouncefunction useDebounce(value, delay) {const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => { const timer = setTimeout(() => setDebouncedValue(value), delay); return () => clearTimeout(timer);}, [value, delay]);
return debouncedValue;}
// useFetchfunction useFetch(url) {const [data, setData] = useState(null);const [loading, setLoading] = useState(true);const [error, setError] = useState(null);
useEffect(() => { fetch(url) .then(res => res.json()) .then(setData) .catch(setError) .finally(() => setLoading(false));}, [url]);
return { data, loading, error };}📋 16.11 Reglas de los Hooks
Section titled “📋 16.11 Reglas de los Hooks”Las 2 reglas
Section titled “Las 2 reglas”// ✅ REGLA 1: Solo en el nivel superiorfunction Componente() {const [a, setA] = useState(); // ✅ OK
if (condicion) { const [b, setB] = useState(); // ❌ MAL}
for (let i = 0; i < 10; i++) { useEffect(() => {}); // ❌ MAL}}
// ✅ REGLA 2: Solo en componentes React o hooks personalizadosfunction Componente() {useState(); // ✅ OK - componente}
function useCustom() {useState(); // ✅ OK - hook personalizado}
function funcionNormal() {useState(); // ❌ MAL - función normal}📝 Resumen final
Section titled “📝 Resumen final”
🐝