Mas Hooks
useContext, useRef, useMemo, useCallback y hooks personalizados
Hooks Adicionales
Ademas de useState y useEffect, React proporciona varios hooks adicionales para casos de uso especificos.
useContext
import { createContext, useContext, useState } from 'react';
// Crear contexto
const TemaContext = createContext();
// Proveedor
function ProveedorTema({ children }) {
const [tema, setTema] = useState('claro');
return (
<TemaContext.Provider value={{ tema, setTema }}>
{children}
</TemaContext.Provider>
);
}
// Consumir con useContext
function BotonTema() {
const { tema, setTema } = useContext(TemaContext);
return (
<button onClick={() => setTema(tema === 'claro' ? 'oscuro' : 'claro')}>
Tema actual: {tema}
</button>
);
}
useRef
import { useRef, useEffect } from 'react';
// Referencia a elemento DOM
function InputConFoco() {
const inputRef = useRef(null);
useEffect(() => {
inputRef.current.focus();
}, []);
return <input ref={inputRef} />;
}
// Valor persistente sin re-renderizado
function Temporizador() {
const contadorRef = useRef(0);
const intervaloRef = useRef(null);
const iniciar = () => {
intervaloRef.current = setInterval(() => {
contadorRef.current += 1;
console.log(contadorRef.current);
}, 1000);
};
const detener = () => {
clearInterval(intervaloRef.current);
};
return (
<div>
<button onClick={iniciar}>Iniciar</button>
<button onClick={detener}>Detener</button>
</div>
);
}
useMemo
import { useMemo, useState } from 'react';
function ListaFiltrada({ items }) {
const [filtro, setFiltro] = useState('');
const [ordenAsc, setOrdenAsc] = useState(true);
// Memoriza el resultado del calculo costoso
const itemsFiltradosYOrdenados = useMemo(() => {
console.log('Calculando lista filtrada...');
return items
.filter(item => item.nombre.includes(filtro))
.sort((a, b) => ordenAsc ? a.nombre.localeCompare(b.nombre) : b.nombre.localeCompare(a.nombre));
}, [items, filtro, ordenAsc]); // Solo recalcula si estas dependencias cambian
return (
<div>
<input value={filtro} onChange={e => setFiltro(e.target.value)} />
<ul>
{itemsFiltradosYOrdenados.map(item => (
<li key={item.id}>{item.nombre}</li>
))}
</ul>
</div>
);
}
useCallback
import { useCallback, useState, memo } from 'react';
// Componente hijo memorizado
const Boton = memo(({ onClick, children }) => {
console.log('Boton renderizado:', children);
return <button onClick={onClick}>{children}</button>;
});
function Padre() {
const [cuenta, setCuenta] = useState(0);
const [texto, setTexto] = useState('');
// Sin useCallback, se crea nueva funcion en cada render
// Con useCallback, la funcion se memoriza
const incrementar = useCallback(() => {
setCuenta(c => c + 1);
}, []);
return (
<div>
<p>Cuenta: {cuenta}</p>
<input value={texto} onChange={e => setTexto(e.target.value)} />
<Boton onClick={incrementar}>Incrementar</Boton>
</div>
);
}
Hooks Personalizados
// Hook personalizado para localStorage
function useLocalStorage(clave, valorInicial) {
const [valor, setValor] = useState(() => {
const guardado = localStorage.getItem(clave);
return guardado ? JSON.parse(guardado) : valorInicial;
});
useEffect(() => {
localStorage.setItem(clave, JSON.stringify(valor));
}, [clave, valor]);
return [valor, setValor];
}
// Uso
function App() {
const [nombre, setNombre] = useLocalStorage('nombre', '');
return (
<input value={nombre} onChange={e => setNombre(e.target.value)} />
);
}
Reglas de los Hooks
- Solo en el nivel superior: No en loops, condiciones o funciones anidadas
- Solo en componentes React: O en hooks personalizados
- Prefijo "use": Los hooks personalizados deben empezar con "use"
Hooks de React 18 y 19
Las versiones recientes de React introducen nuevos hooks poderosos:
useId — IDs Únicos para Accesibilidad
Genera IDs estables únicos para atributos de accesibilidad, seguros para SSR.
import { useId } from "react";
function FormField({ label }) {
const id = useId();
return (
<div>
<label htmlFor={id}>{label}</label>
<input id={id} />
</div>
);
}
useTransition — Actualizaciones No Bloqueantes
Marca actualizaciones de estado como no urgentes para mantener la UI responsiva.
import { useState, useTransition } from "react";
function SearchableList({ items }) {
const [query, setQuery] = useState("");
const [filtered, setFiltered] = useState(items);
const [isPending, startTransition] = useTransition();
const handleChange = (e) => {
setQuery(e.target.value); // Urgente: actualizar input
startTransition(() => {
// No urgente: filtrar lista grande
setFiltered(items.filter(i => i.name.includes(e.target.value)));
});
};
return (
<div>
<input value={query} onChange={handleChange} />
{isPending && <span>Actualizando...</span>}
{filtered.map(item => <div key={item.id}>{item.name}</div>)}
</div>
);
}
useDeferredValue — Valor con Renderizado Diferido
Difiere la actualización de un valor para mantener la UI responsiva durante renderizados pesados.
import { useState, useDeferredValue } from "react";
function SearchResults({ query }) {
const deferredQuery = useDeferredValue(query);
// Usa deferredQuery para renderizado pesado
const results = heavySearch(deferredQuery);
return results.map(r => <div key={r.id}>{r.name}</div>);
}
Para más hooks modernos como useFormStatus, useOptimistic y use(), consulta el tema de React 19.