blense.
InicioFrontendProgramaciónIANoticiasGuíasCheatsheetsAcerca de

Tecnología descifrada, siempre.

Una lectura curada sobre IA, producto y la cultura detrás del código. Sin ruido, sin hype.

blense.

Tecnología con foco. Historias sobre lo que la innovación realmente cambia.

Secciones

FrontendInteligencia artificialProgramación y desarrolloNoticias tech

Blense

Acerca deRSSPrivacidadTérminosCookies
© 2026 Blense · blense.fun
Frontend

6 Técnicas Secretas del useEffect

Más allá del fetch y el event listener. Lo que la documentación muestra — y lo que no explica bien.

Gpor Genildo Souza14 abr6 min de lectura
6 Técnicas Secretas del useEffect

useEffect es el hook más enseñado. Y el más malentendido. No porque la API sea mala — sino porque la mayoría de los tutoriales se detienen en el segundo ejemplo.

Fetch al montar. Event listener. Array de dependencias con la variable correcta. Eso es lo básico, y lo básico está en todos lados.

Lo que no está en todos lados son los seis comportamientos que separan a quienes usan useEffect de quienes lo entienden.

Bugs evitados
6
patrones de error recurrentes
React 19
1
técnica del futuro, disponible hoy
Re-renders
0
innecesarios con Ref como escape

Técnica 01 — Cleanup con AbortController

Cada vez que un componente se desmonta antes de que el fetch termine, la respuesta igual llega — e intenta actualizar el estado de un componente que ya no existe.

React mostraba el aviso en consola: "Can't perform a React state update on an unmounted component". El aviso fue eliminado en React 18. El bug continúa.

MONTA useEffect dispara fetch() DESMONTA ¿cleanup corrió? respuesta llega setState() 💥 SIN abort → bug silencioso CON AbortController → cancelado ✓
Diagrama — Ciclo de vida con fetch asíncrono
JS
function UserProfile({ userId }) {  const [user, setUser] = useState(null);  useEffect(() => {    const controller = new AbortController();    async function fetchUser() {      try {        const res = await fetch(`/api/users/${userId}`, {          signal: controller.signal,        });        const data = await res.json();        setUser(data);      } catch (err) {        if (err.name === 'AbortError') return;        console.error(err);      }    }    fetchUser();    return () => controller.abort();  }, [userId]);  return <div>{user?.name}</div>;}
💡

La función de cleanup corre cuando el componente se desmonta y antes de cada nueva ejecución del efecto — es decir, antes de cada llamada con userId diferente.

Técnica 02 — El array de dependencias es una declaración, no un filtro

El instinto equivocado aparece así: el efecto corre demasiado, entonces el dev elimina una dependencia del array para "detener" las ejecuciones extra. Funciona hasta el día que no funciona.

Arsenal del dev de élite.
Guía de estrategias de carga HTML que acelerará sus tiempos de carga9 minZustand + TanStack Query: La dupla poderosa para la gestión de estado en React11 minDeja de arriesgar tu frontend: la arquitectura definitiva de Angular 2218 minRuntime: El Concepto Que Nadie Te Explicó Bien (Y Que Lo Cambia Todo Cuando Lo Entiendes)10 min

El array de dependencias le dice a React qué valores usa este efecto. No qué valores deben activarlo. Esa distinción lo cambia todo. Omitir una dependencia no silencia el re-run — crea un stale closure: el efecto queda atrapado en un snapshot antiguo del valor, y el bug aparece de forma no determinística.

❌ Stale closure

useEffect(() => {
// count se lee aquí,
// pero fue omitido del array.
// El valor nunca se actualiza.
document.title = `Count: ${count}`;
}, []); // ← mintiendo a React

✅ Declaración honesta

useEffect(() => {
// count está en el array.
// React sabe cuándo re-ejecutar.
document.title = `Count: ${count}`;
}, [count]); // ← contrato honesto

⚠️

eslint-plugin-react-hooks detecta omisiones automáticamente. Deshabilitar la regla exhaustive-deps es esconder el síntoma, no tratar la causa.

Si el efecto corre demasiado, el problema real generalmente es un objeto o función que se recrea en cada render. La solución es estabilizar los valores — con useCallback, useMemo, o moviendo la definición dentro del propio efecto. No omitir.

Técnica 03 — useLayoutEffect: cuando el DOM no puede esperar

useEffect dispara después de que el navegador pinta. En la mayoría de los casos, es exactamente lo que quieres. Pero existe una ventana entre que React confirma los cambios en el DOM y el navegador pinta la pantalla.

En posicionamiento de tooltips, mediciones de layout y animaciones que dependen del tamaño real — useEffect dispara demasiado tarde. El usuario ve el elemento en la posición incorrecta antes de que se corrija.

TIEMPO → useEffect React render DOM commit Pintura useEffect dispara useLayoutEffect React render DOM commit useLayoutEffect Pintura flash visible aquí ↑ síncrono, antes de pintar ✓ ⚠ useLayoutEffect bloquea la pintura. No hagas operaciones lentas aquí.
Diagrama — Timing: useEffect vs useLayoutEffect
JS
function Tooltip({ targetRef, children }) {  const tooltipRef = useRef(null);  const [pos, setPos] = useState({ top: 0, left: 0 });  // Mide ANTES de pintar — sin flash de posición incorrecta  useLayoutEffect(() => {    if (!targetRef.current || !tooltipRef.current) return;    const targetRect = targetRef.current.getBoundingClientRect();    const tooltipRect = tooltipRef.current.getBoundingClientRect();    setPos({      top: targetRect.top - tooltipRect.height - 8,      left: targetRect.left + targetRect.width / 2 - tooltipRect.width / 2,    });  }, [targetRef]);  return (    <div ref={tooltipRef} style={{ position: 'fixed', top: pos.top, left: pos.left }}>      {children}    </div>  );}
ℹ️

useLayoutEffect es síncrono y bloquea la pintura. Resérvalo para mediciones de DOM y posicionamiento. Operaciones lentas aquí congelan la UI.

Técnica 04 — Refs como válvula de escape de dependencias

Las funciones de callback llegan como props. Cuando necesitas llamarlas dentro de un efecto, el linter exige que entren en el array de dependencias. Pero los callbacks recreados en cada render hacen que el efecto se re-ejecute sin motivo real.

La ref es la salida: guarda el valor más reciente sin hacer que el efecto dependa de él.

A) Dependencia directa — re-runs innecesarios Render 1 Render 2 Render 3 re-run re-run ... y así sucesivamente B) Via ref — efecto estable Render 1 Render 2 Render 3 callbackRef siempre actualizado efecto: corre 1x ✓
Diagrama — Callback via ref vs. dependencia directa
JS
function useInterval(callback, delay) {  // Ref siempre con la versión más reciente del callback  const savedCallback = useRef(callback);  // Actualiza la ref sin re-ejecutar el efecto  useEffect(() => {    savedCallback.current = callback;  }, [callback]);  // El interval usa la ref — el array de deps solo necesita [delay]  useEffect(() => {    if (delay === null) return;    const id = setInterval(() => {      savedCallback.current(); // lee el valor más reciente via ref    }, delay);    return () => clearInterval(id);  }, [delay]);}

Técnica 05 — useEffectEvent: leer estado sin depender de él

Un efecto necesita reaccionar a un cambio — roomId — pero dentro de él, necesita leer un valor actual — theme — sin que ese valor cause re-ejecución. React 19 introdujo useEffectEvent para formalizar esta separación entre código reactivo y no-reactivo.

JS
import { experimental_useEffectEvent as useEffectEvent } from 'react';function ChatRoom({ roomId, theme }) {  // onConnected lee theme internamente, pero no depende de él  const onConnected = useEffectEvent(() => {    showNotification(`Conectado a la sala ${roomId}`, theme);  });  useEffect(() => {    const connection = createConnection(roomId);    connection.on('connected', () => onConnected());    connection.connect();    return () => connection.disconnect();  }, [roomId]); // theme no necesita estar aquí}
ℹ️

Estado en abril 2026: useEffectEvent sigue en canary/experimental. En producción, usa el patrón con useRef de la técnica anterior. El concepto es el mismo — la API se estabilizará.

useEffect reacciona a: [roomId] conectar / desconectar reactivo por naturaleza llama useEffectEvent lee: theme (siempre actual) showNotification() no-reactivo — no causa re-run ✓
Diagrama — Separación reactivo / no-reactivo

Técnica 06 — useEffect no gestiona estado derivado

Este es el error silencioso más común. El código funciona. Los tests pasan. Y aun así hay un render extra cada vez — más un useEffect que no debería existir.

JS
// ❌ Effect para derivar estado — causa doble renderfunction FilteredList({ items, query }) {  const [filtered, setFiltered] = useState([]);  useEffect(() => {    setFiltered(items.filter(item => item.name.includes(query)));  }, [items, query]);  return <List items={filtered} />;}// ✅ Calculado en el render — correcto y sin overheadfunction FilteredList({ items, query }) {  const filtered = useMemo(    () => items.filter(item => item.name.includes(query)),    [items, query]  );  return <List items={filtered} />;}

useEffect existe para sincronizar React con sistemas externos: APIs, WebSockets, timers, localStorage, librerías de terceros. No para transformar datos que ya están dentro de React.

useState + useEffect Render 1 lista desactualizada Render 2 lista correcta (ahora) = 2 renders por query change useMemo en el render Render único — correcto ✓ = 1 render por query change
Diagrama — Cantidad de renders
ℹ️

Test rápido: cualquier useEffect que empiece con setSomeState(...) merece sospecha. Pregunta primero: ¿podría este cálculo ocurrir durante el render?

Lo que cambia en tu código a partir de ahora
  • 1Todo fetch dentro de useEffect tiene un AbortController — sin excepciones.
  • 2El array de dependencias es honesto. Cuando incomoda, el problema está en el valor que se recrea, no en la regla de eslint.
  • 3Las mediciones de DOM y posicionamientos usan useLayoutEffect, no useEffect.
  • 4Los callbacks de props que entran en efectos se estabilizan via ref o useCallback.
  • 5useEffectEvent separa lo que reacciona al mundo externo de lo que solo lee el estado interno.
  • 6El estado derivado se calcula en el render. useEffect no es un transformador de datos.

useEffect no ha cambiado mucho desde React 16.8. Lo que cambió es el entendimiento de lo que realmente es: una capa de sincronización entre React y el mundo externo.

Todo lo que no necesita el mundo externo — no pertenece ahí.

Quiz interactivo

Pon a prueba tus conocimientos

Responde y valida tu comprensión.

1. ¿Qué API recomienda el artículo para cancelar peticiones en el cleanup de useEffect?
2. Según el artículo, el array de dependencias es...
3. ¿Cuándo usar useLayoutEffect en lugar de useEffect?
Sigue explorando
¿Quieres más contenido así?

Mira otros artículos en la misma línea y mantén el ritmo.

Ver más artículos
#JavaScript#React

Arsenal del dev de élite.

Guía de estrategias de carga HTML que acelerará sus tiempos de carga
Frontend

Guía de estrategias de carga HTML que acelerará sus tiempos de carga

Descubre cómo optimizar la carga de scripts con defer y async para eliminar bloqueos de renderizado y mejorar la velocidad de tu sitio web.

Genildo Souza · 24 jun · 9 min
Tailwind CSS v4: Cómo acelerar tus builds 100x con la nueva configuración CSS-first
Frontend

Tailwind CSS v4: Cómo acelerar tus builds 100x con la nueva configuración CSS-first

Descubre cómo Tailwind CSS v4 revoluciona el desarrollo frontend con builds ultrarrápidos y una nueva configuración CSS-first sin complicaciones.

Genildo Souza · 20 jun · 16 min
Zustand + TanStack Query: La dupla poderosa para la gestión de estado en React
Frontend

Zustand + TanStack Query: La dupla poderosa para la gestión de estado en React

Descubre por qué Zustand y TanStack Query son la combinación ideal para separar el estado de cliente del servidor y simplificar tu código en React.

Genildo Souza · 18 jun · 11 min
En este artículo
  • Técnica 01 — Cleanup con AbortController
  • Técnica 02 — El array de dependencias es una declaración, no un filtro
  • Técnica 03 — useLayoutEffect: cuando el DOM no puede esperar
  • Técnica 04 — Refs como válvula de escape de dependencias
  • Técnica 05 — useEffectEvent: leer estado sin depender de él
  • Técnica 06 — useEffect no gestiona estado derivado