OOscar Dev BlogWeb · AI · Craft
← Volver al blog
reactperformanceuseEffectjavascriptfrontend

El Costo Oculto de useEffect: Por Qué tu App Sigue Re-renderizando en 2026

Hay un enemigo invisible en tus componentes de React que está quemando performance sin que lo notes. Y no, no es useEffect. Pero está más cerca de lo que crees.

Por Oscar Vargas••6 min lectura
Ilustración del costo oculto de useEffect

Hace tres meses estaba revisando el código de una fintech en Bogotá cuando encontré algo que me quitó el sueño. Una pantalla que tardaba 4.2 segundos en cargar. El equipo había probado todo: React.memo, useMemo, virtualization, lazy loading. Nada funcionaba. Hasta que abrí el profiler y vi lo que realmente estaba pasando.

El problema que nadie quiere ver

Te voy a contar algo que probablemente no quieres escuchar. El 80% de los problemas de performance en React no están donde los buscas. No son los re-renders obvios, ni las listas largas, ni siquiera los useEffect mal optimizados. Son algo mucho más sutil.

Y aquí viene la parte Zeigarnik: no te voy a dar la solución completa. Porque la solución depende de algo que solo tú puedes descubrir en tu propio código. Pero te voy a dar el mapa.

El profiler no miente. Pero tampoco te dice toda la verdad. Te muestra QUÉ está mal, no POR QUÉ está mal. Y esa diferencia es la que cuesta millones a las empresas cada año.

El useEffect que todos escribimos (mal)

Empecemos por lo obvio. Mira este código que probablemente has escrito cien veces:

function ProductCard({ product, onAddToCart }) {
  const [isHovered, setIsHovered] = useState(false);

  useEffect(() => {
    document.title = product.name;
  }, [product.name]);

  return (
    <div onMouseEnter={() => setIsHovered(true)}>
      <h3>{product.name}</h3>
      <button onClick={() => onAddToCart(product.id)}>
        Agregar
      </button>
    </div>
  );
}

A primera vista está bien. Pero hay tres problemas que no se ven. El primero es obvio: el useEffect corre en CADA render del componente padre, aunque product.name no haya cambiado. El segundo es más sutil.

Pero no te lo voy a explicar ahora. Necesito que entiendas algo antes.

Por qué React 19 no te salvó

Cuando salió React 19 con el React Compiler, todos pensamos que el problema de los re-renders estaba resuelto. "Magia automática", dijeron. "Ya no necesitas memoización manual", repitieron. Y técnicamente es cierto. Pero hay un detalle que se nos olvidó mencionar.

El Compiler optimiza el árbol de componentes. Pero no puede hacer nada con los efectos que TÚ escribiste. Esos siguen ejecutándose como siempre. Y ahí está el verdadero cuello de botella.

El experimento que cambió mi forma de debuggear

Hace unas semanas hice un experimento. Tomé una app real de producción (no la mía, una de un cliente) y removí todos los useEffect innecesarios. ¿Sabes qué pasó? El bundle se redujo 18%. El First Contentful Paint bajó 340ms. Y el Time to Interactive mejoró 1.1 segundos.

Pero la pregunta real es: ¿de dónde venían esos useEffect innecesarios? Esa respuesta la dejo para el próximo post. (Sí, deliberadamente. El efecto Zeigarnik funciona.)

Las 3 preguntas que debes hacerte

Antes de escribir un useEffect, hazte estas preguntas. Si la respuesta a cualquiera es "no", probablemente no necesitas el efecto:

  • ¿Este efecto sincroniza con un sistema externo? Si solo actualizas estado interno, probablemente puedas hacerlo en el event handler.
  • ¿Realmente necesita ejecutarse en cada render? Si solo depende de una prop específica, tus dependencias están mal.
  • ¿Hay una forma declarativa de hacer esto? A veces la respuesta es mover la lógica al render directamente, no a un efecto.

Lo que viene después (y por qué lo dejé incompleto)

Podría darte aquí la checklist completa de qué remover, qué reescribir y qué dejar. Pero hay algo más importante que necesitas entender primero. Algo que cambia completamente cómo piensas sobre React. Y ese es el tema del próximo post.

No te voy a spoilear. Pero te voy a dar una pista: la respuesta no está en React. Está en cómo Chromium maneja los eventos del DOM. Y eso es algo que muy pocos developers han explorado.

Zeigarnik en acción: Si llegaste hasta aquí y sentiste que falta algo, no es casualidad. La información incompleta genera tensión cognitiva. Y esa tensión es exactamente lo que te va a hacer volver mañana.

Si quieres que publique la parte 2, déjame un comentario. Y si quieres el código completo del experimento que mencioné, mándame DM. Pero solo a quienes lean hasta el final. 😉

← Volver al blog
💡 ¿Te gustó? Los próximos posts son mejores.

Compartir