Apoya mi contenido: 

Tabla de contenido

Cómo debuggear problemas de renderizado en React (herramientas y técnicas)

Objetivo: detectar por qué un componente renderiza de más, medir el impacto y aplicar correcciones seguras sin romper la experiencia de usuario.

1) Señales de que estás sufriendo renders innecesarios

  • Scroll entrecortado, animaciones “lag”.
  • Inputs que se sienten “pesados”.
  • Uso de CPU alto en pestañas inactivas.
  • La vista cambia aunque los datos no.

2) Herramientas imprescindibles

React DevTools

Instala la extensión y abre la pestaña Components para ver qué renderiza y por qué. Activa Highlight updates when components render para resaltar el DOM que se actualiza.

Profiler de React

Desde la pestaña Profiler graba una interacción. Revisa el flamegraph y ordena por “commit duration” para ubicar cuellos de botella. Usa la vista Ranked para localizar los componentes más costosos.

Chrome DevTools: Performance & Memory

  • Performance: registra con CPU throttling (x4) para amplificar problemas.
  • Coverage: detecta JS/CSS no utilizado que incrementa el trabajo de render.
  • Memory: observa snapshots para objects retenidos por closures o refs.

Linters y diagnósticos

  • eslint-plugin-react-hooks para dependencias correctas.
  • why-did-you-render para avisar cuando un componente vuelve a renderizar con las mismas props/estado.

3) Checklist de causas comunes y cómo resolverlas

3.1 Props y estado inestables

Síntoma: pasas objetos/arrays/funciones creadas en cada render.

function Parent({ items }) {
  const onSelect = (id) => setSelected(id); // se recrea cada render
  return <List items={items} onSelect={onSelect} />;
}

Solución: estabiliza referencias con useCallback y useMemo.

const onSelect = useCallback((id) => setSelected(id), []);
const stableData = useMemo(() => ({ foo: 1 }), []);

3.2 Falta de memoización de componentes puros

Si un componente renderiza con las mismas props, envuélvelo en React.memo:

const Row = React.memo(function Row({ item, onClick }) {
  /* ... */
});

Para props complejas, añade areEqual personalizado o normaliza datos antes.

3.3 Estado global que dispara renders masivos

Evita que todo el árbol dependa de un mismo contexto. Divide contextos o usa librerías con selectors (ej.: zustand) para suscribirte a fragmentos.

3.4 Keys inestables

Usar índices como clave en listas puede provocar renders y errores al reordenar. Prefiere IDs estables.

3.5 Efectos que actualizan estado en cada render

useEffect(() => {
  setWidth(window.innerWidth); // corre en cada render si depende de algo inestable
});

Añade dependencias correctas o usa useLayoutEffect sólo cuando sea imprescindible.

3.6 Derivar estado de props

Si calculas estado a partir de props en cada render, considera useMemo o useMemo + useEffect sólo cuando la prop cambie.

3.7 Re-render por cambios de estilo

Evita crear objetos de estilo inline nuevos. Usa clases o memoriza los objetos.

4) Patrón de diagnóstico en 7 pasos

  1. Reproduce el problema con un caso mínimo.
  2. Mide con el Profiler (baseline de ms por interacción).
  3. Detecta el componente más caro (flamegraph).
  4. Compara props/estado entre render previo y actual.
  5. Estabiliza referencias (useCallback/useMemo/React.memo).
  6. Vuelve a medir y verifica mejora >= 20–30%.
  7. Refactoriza zonas calientes (virtualización, pagination, split de context).

5) Técnicas de optimización efectivas

  • Virtualización con react-window o react-virtual para listas largas.
  • Suspense + useTransition/useDeferredValue para suavizar interacciones intensas.
  • Code-splitting con React.lazy y bundlers modernos.
  • Evitar trabajo en render: mueve cálculos pesados a web workers.
  • Server Components / SSR para reducir trabajo del cliente cuando aplique.

6) Ejemplos cortos de “antes/después”

Funciones inline vs useCallback

// ❌ antes
<Button onClick={() => doSomething(value)} />

// ✅ después
const handleClick = useCallback(() => doSomething(value), [value]);
<Button onClick={handleClick} />

Objeto de estilos estable

// ✅ memoriza el objeto
const boxStyle = useMemo(() => ({ padding: 8, borderRadius: 8 }), []);
<div style={boxStyle} />

7) Cómo leer el Profiler como pro

  • Commit: conjunto de cambios aplicados al DOM virtual.
  • Foco en picos: busca commits con duración inusual.
  • “Why did this render?”: en DevTools observa cambios de props/estado que dispararon el render.

8) Errores frecuentes y cómo evitarlos

  • Dep arrays incompletos en useEffect → usa el linter para forzar dependencias correctas.
  • Context monolítico → separa proveedores por dominio (tema, usuario, permisos).
  • Keys inestables en listas → usa IDs reales.
  • Recrear selectores/filtrados pesados en cada render → memoiza o precalcula.

9) Utilidades recomendadas

  • why-did-you-render (diagnóstico de renders redundantes).
  • react-window / react-virtual (virtualización).
  • eslint-plugin-react-hooks (disciplina en hooks).

10) Checklist final (copiar/pegar)

  • ¿Tengo funciones/objetos/arrays inline? → useCallback/useMemo.
  • ¿Componentes puros? → React.memo.
  • ¿Contextos divididos por dominio?
  • ¿Keys estables en listas?
  • ¿Virtualización para listas > 100 ítems?
  • ¿Profiler muestra mejora tras cambios?
  • ¿Linters y WDYR activos en dev?

Consejo: optimiza sólo después de medir. La micro-optimización prematura complica el código sin beneficios reales.

¡Comunícate con nosotros!