Un conjunto de ganchos personalizados reutilizables para cada nuevo proyecto React
Explore un conjunto seleccionado de ganchos personalizados de React que abarcan almacenamiento, atenuación de eventos, clics y obtención de datos, y que eliminan el código repetitivo en nuevos proyectos.
Los hooks de React se han convertido en la forma estándar para gestionar el estado, los efectos secundarios y la lógica reutilizable en las aplicaciones modernas, pero conocer los hooks integrados es solo la mitad de la historia. Igualmente valioso es contar con un conjunto personal de hooks personalizados, pequeños y bien probados, que se puedan incorporar a cualquier proyecto nuevo para evitar reconstruir las mismas utilidades desde cero. Este artículo primero presenta un conjunto de hooks que vale la pena incluir en su kit inicial, y luego profundiza en cómo funcionan realmente las APIs de los hooks subyacentes para que entienda no solo qué copiar, sino también por qué se comportan de esa manera.
Al iniciar un nuevo proyecto React, ciertos hooks tienden a reaparecer casi cada vez. Algunos abordan problemas de rendimiento, otros mejoran la experiencia del usuario, y varios simplemente impiden que tengas que reescribir la misma lógica básica en cada código. Con el paso de los proyectos, estas herramientas se convierten en un conjunto estándar de inicio: en lugar de resolver el mismo problema desde cero, se utiliza el hook, se modifica si el proyecto necesita algo ligeramente diferente, y se vuelve a trabajar en la creación de funcionalidades reales. A continuación se presentan doce hooks que encajan en esa descripción.
El primero es useLocalStorage, útil porque casi todas las aplicaciones necesitan conservar algo entre recargas, ya sea una preferencia de tema, un campo de formulario u otras configuraciones del usuario.
import { useState } from "react";
function useLocalStorage(key, initialValue) {
const [value, setValue] = useState(() => {
const saved = localStorage.getItem(key);
return saved ? JSON.parse(saved) : initialValue;
}); const updateValue = (newValue) => {
setValue(newValue);
localStorage.setItem(key, JSON.stringify(newValue));
}; return [value, updateValue];
}
Este patrón se utiliza comúnmente para almacenar cosas como la preferencia de modo oscuro, un token de autenticación o cualquier configuración que se desee recordar entre visitas.
A continuación está useToggle, para el caso habitual en el que un estado simplemente cambia entre true y false.
import { useState } from "react";
function useToggle(initial = false) {
const [value, setValue] = useState(initial); const toggle = () => setValue(v => !v); return [value, toggle];
}
Es ideal para modales, menús desplegables, barras laterales y otros elementos de interfaz con funcionalidad de alternancia.
useDebounce se emplea constantemente en campos de búsqueda, donde no se desea enviar una solicitud con cada tecla pulsada.
import { useState, useEffect } from "react";
function useDebounce(value, delay = 500) {
const [debounced, setDebounced] = useState(value); useEffect(() => {
const timer = setTimeout(() => {
setDebounced(value);
}, delay); return () => clearTimeout(timer);
}, [value, delay]); return debounced;
}
Al retrasar la actualización hasta que cesa la escritura, se reducen las llamadas API innecesarias.
useWindowSize permite que los diseños responsivos tengan acceso a las dimensiones actuales de la ventana de visualización.
import { useState, useEffect } from "react";
function useWindowSize() {
const [size, setSize] = useState({
width: window.innerWidth,
height: window.innerHeight
}); useEffect(() => {
const resize = () =>
setSize({
width: window.innerWidth,
height: window.innerHeight
}); window.addEventListener("resize", resize); return () => window.removeEventListener("resize", resize);
}, []); return size;
}
usePrevious es útil siempre que necesitas comparar un valor con el que tenía en la última renderización.
import { useEffect, useRef } from "react";
function usePrevious(value) {
const ref = useRef(); useEffect(() => {
ref.current = value;
}, [value]); return ref.current;
}
Esto es particularmente útil para animaciones o para detectar cuándo algo realmente ha cambiado.
useClickOutside cierra elementos de interfaz como modales cuando el usuario hace clic en cualquier lugar fuera de ellos, lo cual coincide con la forma en que los usuarios esperan intuitivamente que se comporten estos componentes.
import { useEffect } from "react";
function useClickOutside(ref, callback) {
useEffect(() => {
function handleClick(e) {
if (ref.current && !ref.current.contains(e.target)) {
callback();
}
} document.addEventListener("mousedown", handleClick); return () =>
document.removeEventListener("mousedown", handleClick);
}, [ref, callback]);
}
Funciona bien para menús desplegables, popovers y menús de navegación móvil.
useDocumentTitle mantiene el título de la pestaña del navegador sincronizado con la vista actual, lo que ayuda en la navegación y orientación.
import { useEffect } from "react";
function useDocumentTitle(title) {
useEffect(() => {
document.title = title;
}, [title]);
}
En lugar de duplicar el mismo efecto en muchos componentes, se llama a este único hook dondequiera que una página necesite un título personalizado.
useFetch encapsula la obtención básica de datos en un hook reutilizable.
import { useState, useEffect } from "react";
function useFetch(url) {
const [data, setData] = useState(null); useEffect(() => {
fetch(url)
.then(res => res.json())
.then(setData);
}, [url]); return data;
}
Para proyectos más complejos que los pequeños, herramientas como React Query o SWR suelen ser una mejor opción, pero esta versión ligera es suficiente para poner en marcha una aplicación sencilla.
useCopyToClipboard aborda el botón de copiar que ahora es omnipresente.
function useCopyToClipboard() {
const copy = (text) => {
navigator.clipboard.writeText(text);
};
return copy;
}
Es ideal para compartir enlaces de invitación, códigos de cupón o claves API con un solo clic.
useOnlineStatus permite que tu aplicación reaccione cuando se interrumpe o se restablece la conexión a la red.
import { useState, useEffect } from "react";
function useOnlineStatus() {
const [online, setOnline] = useState(navigator.onLine); useEffect(() => {
window.addEventListener("online", () => setOnline(true));
window.addEventListener("offline", () => setOnline(false));
}, []); return online;
}
Es una pequeña adición, pero mejora notablemente la experiencia de uso de la aplicación cuando la conectividad es inestable.
useDarkMode gestiona el cambio de modo oscuro que la mayoría de los usuarios esperan en las aplicaciones modernas hoy en día.
import { useEffect } from "react";
function useDarkMode(enabled) {
useEffect(() => {
document.body.classList.toggle("dark", enabled);
}, [enabled]);
}
Se utiliza comúnmente junto con useLocalStorage para que el modo elegido persista entre sesiones.
Finalmente, useTimeout hace que el trabajo con retrasos sea mucho más ordenado que dispersar llamadas directas a setTimeout en los componentes.
import { useEffect } from "react";
function useTimeout(callback, delay) {
useEffect(() => {
const timer = setTimeout(callback, delay); return () => clearTimeout(timer);
}, [callback, delay]);
}
Es adecuado para notificaciones, pantallas de bienvenida y cualquier acción que necesite ejecutarse después de un retraso.
En muchos proyectos de React, hay una lección que se mantiene constante: no es necesario reconstruir todo desde cero cada vez. Mantener una pequeña biblioteca de ganchos reutilizables acelera el desarrollo, hace que los componentes sean más fáciles de leer y elimina el código repetitivo. Ninguno de estos doce ganchos es especialmente complejo, pero juntos ahorran una cantidad considerable de tiempo a lo largo de un proyecto. A medida que crezcan tus propios proyectos, es probable que acumules una colección personal similar; lo importante no es la cantidad total de ganchos que tengas, sino si resuelven los problemas con los que te encuentras una y otra vez. Una pequeña utilidad que escribas hoy para un proyecto a menudo se convierte en algo al que recurrirás en todos los proyectos posteriores.
Comprender una biblioteca personal de ganchos es útil en el día a día, pero también es igualmente valioso entender la mecánica que subyace en ellos: qué resuelve cada gancho principal, cuándo usarlo y cómo se comporta en las diferentes renderizaciones. Esta base más profunda es donde el uso de los ganchos deja de ser una simple copia y pegado para convertirse en una comprensión real.
Antes de los ganchos, los componentes funcionales no podían almacenar estado ni ejecutar efectos secundarios, por lo que esa lógica residía en los componentes de clase con un objeto state y métodos de ciclo de vida:
class Counter extends React.Component {
state = {
count: 0
};
increment = () => {
this.setState({
count: this.state.count + 1
});
};
render() {
return (
<button onClick={this.increment}>
{this.state.count}
</button>
);
}
}
Con los ganchos, el mismo componente se convierte en una función sencilla que llama directamente a useState:
function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(c => c + 1)}>
{count}
</button>
);
}
La versión funcional suele ser más fácil de leer y reutilizar.
Hay dos reglas que rigen los ganchos. Primero, siempre llámalos en el nivel superior; nunca dentro de condicionales, bucles o funciones anidadas. Evita esto:
if (isLoggedIn) {
useEffect(() => {
// ❌
}, []);
}
for (...) {
useState(0); // ❌
}
En su lugar, mantenga la llamada al Hook sin condiciones y coloque la lógica condicional después:
function Component() {
const [count, setCount] = useState(0);
if (count > 10) {
// Normal conditional logic is fine
}
return ...;
}
Esta regla existe porque React rastrea el estado por orden de llamadas, no por nombre. Dadas dos llamadas a useState:
function Component() {
const [count] = useState(0);
const [name] = useState("Salim");
return ...;
}
React las ordena según su posición:
Hook #1 → count
Hook #2 → name
Si la primera llamada se vuelve condicional:
if (condition) {
useState(0);
}
useState("Salim");
el orden puede cambiar entre renders:
Render 1:
Hook #1 → count
Hook #2 → name
Render 2:
Hook #1 → name
Una vez que esto ocurre, React ya no puede asociar el estado almacenado con la llamada correcta, por lo que el orden debe permanecer fijo en cada render.
useState permite que un componente recuerde un valor entre renders:
const [count, setCount] = useState(0);
Devuelve un par:
count → current state
setCount → state update function
Un contador básico muestra este patrón:
function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(c => c + 1)}>
Count: {count}
</button>
);
}
Al hacer clic en el botón se activa esta secuencia:
Click
↓
setCount()
↓
React schedules update
↓
Component renders again
↓
New state is calculated
↓
DOM is updated if necessary
El estado no es una variable ordinaria como let count = 0, ya que esta no sobreviviría a la reejecución de la función. React lo almacena fuera del componente y proporciona el valor correcto en cada renderizado:
Render 1
count = 0
Render 2
count = 1
Render 3
count = 2
El componente se vuelve a ejecutar, pero React conserva el estado entre las ejecuciones.
Esto es importante cuando se actualiza el estado varias veces en un mismo manejador:
setCount(count + 1);
setCount(count + 1);
setCount(count + 1);
Si el renderizado comenzó con count en 0, las tres líneas utilizan esa misma instantánea, lo que da como resultado:
setCount(1)
setCount(1)
setCount(1)
en lugar de tres incrementos reales. La solución es una actualización funcional:
setCount(c => c + 1);
setCount(c => c + 1);
setCount(c => c + 1);
Ahora React aplica cada actualización de forma secuencial, ya que cada actualizador recibe el valor más reciente; esto es útil siempre que el nuevo estado dependa del estado anterior.
useEffect sincroniza un componente con algo fuera de React:
useEffect(() => {
// synchronization logic
}, [dependencies]);
Los casos típicos incluyen llamadas a API, temporizadores, listeners de eventos, WebSockets, otras APIs del navegador, suscripciones y bibliotecas de terceros. Ejemplo:
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(setUser);
}, [userId]);
return <h1>{user?.name}</h1>;
}
Este efecto se sincroniza con userId; cuando este cambia, el efecto debe ejecutarse nuevamente.
Ese comportamiento proviene del array de dependencias. Dado:
useEffect(() => {
console.log(count);
}, [count]);
React compara las dependencias de forma conceptual:
Previous dependencies
↓
New dependencies
↓
Compare
↓
Changed?
Si difieren, el efecto se ejecuta de nuevo:
Previous: [1]
New: [2]
→ Effect needs to run
Si coinciden, se omite su ejecución:
Previous: [2]
New: [2]
→ Effect can be skipped
La comparación sigue Object.is, no la igualdad profunda.
Los efectos pueden devolver una función de limpieza, que se ejecuta antes del siguiente efecto y al desmontar el componente:
useEffect(() => {
const timer = setInterval(() => {
console.log("tick");
}, 1000);
return () => {
clearInterval(timer);
};
}, []);
La limpieza es importante para cosas como:
Timers
Event listeners
Subscriptions
WebSocket connections
Abortable requests
Cuando cambian las dependencias, React elimina el efecto anterior antes de ejecutar el nuevo; también lo hace cuando el componente se desmonta.
Un campo de búsqueda ilustra esto en la práctica: cada tecla pulsada puede generar una solicitud:
react
react hooks
react performance
Dado que una respuesta obsoleta podría sobrescribir a una más reciente, se debe cancelar la solicitud anterior:
useEffect(() => {
const controller = new AbortController();
fetch(`/api/search?q=${query}`, {
signal: controller.signal
})
.then(res => res.json())
.then(setResults)
.catch(error => {
if (error.name !== "AbortError") {
console.error(error);
}
});
return () => {
controller.abort();
};
}, [query]);
El ciclo de vida se ve así:
query changes
↓
cleanup previous effect
↓
abort previous request
↓
start new request
Los campos de búsqueda reales suelen combinar esto con el debouncing.
Un error común es usar useEffect para valores que se pueden obtener directamente:
const [fullName, setFullName] = useState("");
useEffect(() => {
setFullName(`${firstName} ${lastName}`);
}, [firstName, lastName]);
Dado que fullName proviene directamente de valores existentes, se puede omitir por completo el efecto y el estado:
const fullName = `${firstName} ${lastName}`;
La versión con efecto añade una sincronización innecesaria y un renderizado adicional. Regla general: si se puede calcular durante el renderizado, no se necesita un efecto para ello.
useRef proporciona un objeto estable cuyo .current permanece constante entre renders sin provocar nuevos renders cuando cambia:
const ref = useRef(initialValue);
Un uso común es hacer referencia a un nodo DOM, como por ejemplo centrar la atención en un campo de entrada:
function Input() {
const inputRef = useRef(null);
function focusInput() {
inputRef.current?.focus();
}
return (
<>
<input ref={inputRef} />
<button onClick={focusInput}>
Focus
</button>
</>
);
}
useRef también es útil para almacenar valores mutables que en absoluto pertenecen a la salida renderizada, como por ejemplo un identificador de temporizador:
const timerRef = useRef(null);
Luego se le asigna directamente, de la misma manera que se haría con cualquier propiedad de un objeto:
timerRef.current = setInterval(...);
La mutación
ref.current
no provoca por sí sola un nuevo render. Contraste estos dos modelos mentales:
useState
→ update → render
useRef
→ mutate .current → no render
Esto hace que los refs sean adecuados para valores que necesitan persistir entre renders pero que no deberían influir en lo que se muestra en la pantalla.
useMemo adopta un enfoque diferente: almacena en caché el resultado de un cálculo en lugar de una referencia DOM.
const result = useMemo(() => {
return expensiveCalculation(data);
}, [data]);
Imagínelo así: useMemo = cachear un cálculo. Un caso de uso típico es filtrar una lista solo cuando los datos subyacentes realmente cambian:
function ProductList({ products, search }) {
const filteredProducts = useMemo(() => {
return products.filter(product =>
product.name
.toLowerCase()
.includes(search.toLowerCase())
);
}, [products, search]);
return <ProductGrid products={filteredProducts} />;
}
Si alguna parte de estado no relacionada se actualiza, React puede simplemente devolver el valor calculado anteriormente siempre y cuando las dependencias sean las mismas.
Conceptualmente, React mantiene un registro pequeño asociado a la llamada al hook:
Hook
├── memoized value
└── dependencies
En la primera renderización:
data = A
calculate(A)
↓
result = X
En una renderización posterior, si data sigue siendo A, la comparación de dependencias es exitosa y React reutiliza X sin volver a calcularlo. Solo cuando data pasa a ser algo como B, React vuelve a realizar el cálculo.
Dicho esto, useMemo es fácil de sobreutilizar. Envolver algo de esta manera:
const fullName = useMemo(
() => `${firstName} ${lastName}`,
[firstName, lastName]
);
rara vez vale la pena: el cálculo es trivial y la memorización en sí no es gratuita; añade sobrecarga y código adicional del que hay que ocuparse. Utiliza useMemo cuando el cálculo sea realmente costoso, cuando necesites una referencia estable a un objeto o array, o cuando el análisis de rendimiento (o un razonamiento claro) demuestre que realmente ayuda.
useCallback es la herramienta equivalente para referencias de funciones en lugar de valores.
const handleClick = useCallback(() => {
doSomething();
}, []);
Esto es importante porque las funciones ordinarias se recrean en cada renderizado:
function Parent() {
const handleClick = () => {
console.log("clicked");
};
return <Child onClick={handleClick} />;
}
En el primer renderizado handleClick apunta a una instancia de función; en el segundo renderizado apunta a otra diferente, por lo que las dos no son iguales aunque hagan lo mismo.
Esa desigualdad se convierte en un problema cuando un componente hijo está envuelto en React.memo:
const Child = React.memo(function Child({ onClick }) {
console.log("Child rendered");
return (
<button onClick={onClick}>
Click
</button>
);
});
y el padre se ve así:
function Parent() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
console.log("clicked");
}, []);
return (
<>
<button onClick={() => setCount(c => c + 1)}>
{count}
</button>
<Child onClick={handleClick} />
</>
);
}
Cuando count cambia:
Parent renders
↓
handleClick reference remains stable
↓
React.memo checks Child props
↓
onClick unchanged
↓
Child can skip rendering
Sin useCallback, la referencia de la función recién creada sería considerada “nueva” por el componente hijo memorizado, lo que causaría que se vuelva a renderizar aunque no haya cambiado nada relevante para él.
Aun así, useCallback no es algo que deba aplicarse por defecto a todas las funciones. Antes de envolver un manejador, pregúntese si realmente hay algo que se beneficie de una referencia estable; por ejemplo:
React.memo child
Dependency array
Expensive downstream computation
Si ninguno de estos casos aplica, generalmente está bien permitir que la función se cree nuevamente de forma normal.
La diferencia entre estos dos hooks radica en lo que memorizan:
useMemo
→ memoizes a VALUE
useCallback
→ memoizes a FUNCTION
Conceptually:
useMemo(() => calculateValue(), deps);
useCallback(() => doSomething(), deps);
Al pasar de los ganchos orientados al rendimiento a uno estructural, la API Context aborda un problema diferente: pasar datos a través de muchas capas de componentes anidados. Sin ella, un valor como el usuario actual podría necesitar recorrer cada nivel intermedio:
App
↓
Navbar
↓
UserMenu
↓
Profile
↓
Avatar
lo que da como resultado algo similar a:
<App user={user} />
<Navbar user={user} />
<UserMenu user={user} />
<Profile user={user} />
<Avatar user={user} />
Este patrón de reenvío de propiedades a través de componentes que de otro modo no las necesitan se conoce como prop drilling.
La configuración del contexto comienza con una llamada de creación:
const UserContext = createContext(null);
Luego se proporciona un valor mediante un proveedor:
function App() {
const user = {
name: "Salim",
role: "Developer"
};
return (
<UserContext.Provider value={user}>
<Dashboard />
</UserContext.Provider>
);
}
y se lee donde sea necesario:
function Profile() {
const user = useContext(UserContext);
return <h1>Hello {user.name}</h1>;
}
Profile ya no necesita que user se propague a través de cada componente intermedio.
Un caso común en el mundo real es la tematización:
const ThemeContext = createContext(null);
function App() {
const [theme, setTheme] = useState("light");
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
<Dashboard />
</ThemeContext.Provider>
);
}
Cualquier componente puede entonces leer el tema actual directamente:
function Button() {
const { theme } = useContext(ThemeContext);
return (
<button className={theme}>
Submit
</button>
);
}
generando un árbol con una forma aproximadamente similar a:
App
│
└── ThemeProvider
│
└── Dashboard
│
└── Button
Button obtiene el valor del tema sin necesidad de explorar propiedades en profundidad.
Es importante aclarar que Context no es automáticamente una solución completa de gestión de estado. Responde a una pregunta específica: ¿cómo hacer que un valor sea accesible para componentes ubicados en profundidad dentro del árbol? No proporciona las herramientas adicionales —selectores, middleware, actualizaciones estructuradas— que ofrece una biblioteca dedicada. Para necesidades de estado más complejas, podría ser necesario recurrir a:
Redux
Zustand
Jotai
Reducer + Context
Context debe considerarse mejor como un mecanismo de distribución de valores, no como un gestor de estado.
También tiene implicaciones para el renderizado. Dado que:
<ThemeContext.Provider value={theme}>
Cada vez que cambia el valor de ese contexto, los componentes que lo consumen pueden volver a renderizarse. El contexto no evita mágicamente las nuevas renderizaciones; incluir un objeto grande que cambia con frecuencia en un contexto ampliamente utilizado puede incluso generar trabajo innecesario. Mejores candidatos para el contexto son valores que cambian con poca frecuencia, como:
Theme
Locale
Authentication information
Feature flags
Application configuration
Los Custom Hooks permiten encapsular lógica reutilizable creada a partir de los hooks integrados. Un ejemplo mínimo:
function useCounter() {
const [count, setCount] = useState(0);
function increment() {
setCount(c => c + 1);
}
return {
count,
increment
};
}
Se utilizan de esta manera:
function Counter() {
const { count, increment } = useCounter();
return (
<button onClick={increment}>
Count: {count}
</button>
);
}
El verdadero valor aquí es reutilizar la lógica, no el marcado.
Es importante destacar que los Custom Hooks comparten lógica, no estado. Si dos componentes separados llaman al mismo Custom Hook:
const counterA = useCounter();
const counterB = useCounter();
no terminan compartiendo un único count. Cada llamada tiene su propio estado independiente. Conceptualmente:
Component A
└── useCounter
└── useState → State A
Component B
└── useCounter
└── useState → State B
Un Hook personalizado encapsula el comportamiento, no un almacén de datos compartido.
Como ejemplo concreto, imagine que una aplicación necesita mostrar si el usuario tiene actualmente una conexión a red. En lugar de duplicar la lógica del receptor de eventos del navegador en cada componente que la necesite, puede envolverla en un Hook personalizado:
function useOnlineStatus() {
const [isOnline, setIsOnline] = useState(
navigator.onLine
);
useEffect(() => {
function handleOnline() {
setIsOnline(true);
}
function handleOffline() {
setIsOnline(false);
}
window.addEventListener("online", handleOnline);
window.addEventListener("offline", handleOffline);
return () => {
window.removeEventListener("online", handleOnline);
window.removeEventListener("offline", handleOffline);
};
}, []);
return isOnline;
}
Cualquier componente que necesite el estado de conectividad ahora puede consumirlo directamente:
function Navbar() {
const isOnline = useOnlineStatus();
return (
<span>
{isOnline ? "🟢 Online" : "🔴 Offline"}
</span>
);
}
Otro componente podría usar el mismo Hook para cambiar por completo su propio renderizado:
function Checkout() {
const isOnline = useOnlineStatus();
if (!isOnline) {
return <p>You are offline.</p>;
}
return <PaymentForm />;
}
Un patrón similar se aplica a la atenuación de entradas que cambian rápidamente:
function useDebounce(value, delay) {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const timer = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => {
clearTimeout(timer);
};
}, [value, delay]);
return debouncedValue;
}
Se utiliza dentro de una función de búsqueda como esta:
function Search() {
const [query, setQuery] = useState("");
const debouncedQuery = useDebounce(query, 500);
useEffect(() => {
if (!debouncedQuery) return;
// Search API
}, [debouncedQuery]);
return (
<input
value={query}
onChange={e => setQuery(e.target.value)}
/>
);
}
lo cual produce esta secuencia de eventos:
User types
↓
query changes
↓
500ms wait
↓
debouncedQuery changes
↓
API request
Estos componentes básicos están diseñados para combinarse, no para usarse de forma aislada:
React Component
│
┌───────────────┼────────────────┐
│ │ │
useState useEffect useContext
│ │ │
UI state External systems Shared data
│
└───────────────┐
│
Custom Hook
│
┌─────────┼─────────┐
▼ ▼ ▼
useState useEffect useMemo
Por ejemplo, un Hook personalizado useProducts() podría depender internamente de:
useState
+
useEffect
+
useMemo
al obtener el estado de autenticación desde useContext. Las recargas siguen un patrón predecible:
State / Props / Context change
↓
React update
↓
Render
↓
Reconciliation
↓
Commit
↓
DOM update
↓
Browser paint
cada Hook desempeñando un papel distinto, resumido aquí:
useState
→ provides state + schedules updates
useMemo
→ calculates/reuses values during render
useCallback
→ calculates/reuses function references during render
useContext
→ reads context during render
useEffect
→ synchronizes with external systems after commit
Lecturas relacionadas
- Construyendo un modelo mental para React: Reconciliación, estado y Hooks — Aprende la lógica detrás de los conceptos fundamentales de React—reconciliación, componentes, props, estado y hooks—para desarrollar intuición en lugar de memorizar APIs.
catch () lanza un Error de Sintaxis en JavaScript — Entienda por qué una lista vacía de parámetros en catch interrumpe completamente el análisis del código JavaScript, y vea las dos formas correctas desde el punto de vista gramatical para escribir un bloque catch sin parámetros.