Depuración de Frontend en Tiempo de Ejecución: Inspeccionar Estado en Vivo Antes de Releer el Código Fuente
Los datos son correctos en la red, pero la UI es incorrecta. Esto es un error de tiempo de ejecución/temporización, y el instinto que te hará perder el tiempo es releer los archivos fuente. Resístelo. Reordena el reflejo.
Cuándo Usarlo
Invoca esta habilidad durante la depuración, cuando aparezca el síntoma, no al inicio de la tarea. Úsala cuando TODAS estas condiciones se cumplan:
- Un componente de React no renderiza nada, contenido obsoleto o vacío.
- Los datos que necesita son correctos en el origen (la API/backend devuelve la respuesta correcta).
- La brecha está en algún lugar entre "respuesta correcta en la red" y "renderizado correcto en pantalla".
Frases típicas: "la API devuelve los datos correctos pero el componente está en blanco", "react-query está atascado", "la solicitud es 200 pero la UI nunca se actualiza", "funciona después de un refresco pero no en la primera carga".
Para qué NO sirve
- Errores de compilación / compilación — eso es un error de la cadena de herramientas, no de tiempo de ejecución.
- Problemas solo de estilo / maquetación — los datos se renderizan, solo se ven mal.
- La API realmente devuelve datos incorrectos o ningún dato — eso es un error normal de backend/contrato. Verifica PRIMERO la respuesta de red; si la respuesta es realmente incorrecta o vacía, detente y corrige el backend/contrato. Esta habilidad es solo para cuando la red es correcta pero la UI no.
El Reordenamiento (la lección clave)
-
Inspecciona el estado de ejecución en vivo ANTES de releer el código fuente. El reflejo es releer el componente, esquema, diseño y cableado del proveedor buscando el error. Cuando los datos son correctos en la red pero la UI es incorrecta, ese reflejo es una trampa: el error frecuentemente NO está en un solo archivo fuente sino en una interacción de tiempo de ejecución/temporización entre piezas que son individualmente correctas. La revisión del código fuente es estructuralmente ciega a esa clase de errores. Mira primero el estado en vivo.
-
Un 200 en el panel de red puede ser activamente engañoso. Una solicitud que muestra 200 significa que la respuesta llegó. NO significa que los datos llegaron al componente o se confirmaron en la caché. La respuesta puede llegar y luego ser descartada, eliminada o nunca confirmada. No concluyas "la petición funciona" a partir de un 200 — confirma que los datos realmente alcanzaron el estado del componente consumidor.
-
Para react-query, inspecciona el observador de consulta en vivo del componente afectado, no solo la red. Lee
status,fetchStatus,dataUpdateCountyerrordel observador.- Una consulta atascada con
status: "pending"/fetchStatus: "fetching"ydataUpdateCount: 0significa que el resultado de la promesa del queryFn nunca se confirmó en el observador. Eso apunta a que la consulta fue eliminada o invalidada a mitad de vuelo — NO a un error de petición. - Confirma que el queryFn en sí está sano forzando un refetch del observador. Si un refetch manual se resuelve correcta e inmediatamente, entonces la petición/cliente/esquema están bien, y el verdadero error es que la consulta inicial fue abandonada (eliminada por una limpieza de caché, invalidada por un efecto, o desmontada por un remontaje). Esta única observación distingue "la ruta de datos está rota" de "la ruta de datos está bien pero la consulta fue eliminada" — errores completamente diferentes.
- Una consulta atascada con
-
Una vez localizado como "consulta abandonada a mitad de vuelo", busca corriente arriba en la gestión del ciclo de vida/caché. La causa es algo que se ejecutó en el mismo momento en que comenzó la consulta: un
removeQueries/invalidateQueries/ reinicio de caché, o una condición de carrera de montaje/desmontaje/remontaje. Mira en efectos de transición de autenticación/sesión, purgas de cambio de principal/inquilino y remontajes de proveedores — NO en el código de petición.
Ruta de Decisión
¿UI incorrecta, datos correctos en el origen?
→ confirma que la respuesta de red es realmente correcta (no es trabajo de esta habilidad si no lo es)
→ lee el observador de consulta en vivo del componente afectado
¿status pending + fetchStatus fetching + dataUpdateCount 0?
→ fuerza un refetch en ese observador
¿el refetch se resuelve instantánea y correctamente?
→ queryFn está BIEN. Error = consulta inicial abandonada.
Busca corriente arriba: removeQueries/invalidateQueries/reset,
condición de carrera de remontaje/desmontaje, efecto de purga de autenticación/sesión/inquilino.
¿el refetch también falla?
→ la ruta de datos está realmente rota. Inspecciona queryFn/cliente/esquema.
¿tiene datos pero la UI sigue en blanco?
→ error de renderizado/selector/condicional en el componente, ahora relee el código fuente.
Herramientas de Inspección en Vivo (pistas, no APIs estables)
Estas alcanzan el observador de react-query de un componente desde una página en ejecución recorriendo el árbol de fibras de React desde un elemento DOM renderizado, leen el estado actual de la consulta del observador y fuerzan un refetch para demostrar que el queryFn funciona.
Estos fragmentos son sensibles a la versión. Fueron derivados contra @tanstack/react-query v5.90 y los internos de fibras de React 18, dependen de elementos internos no documentados y SE DESVIARÁN. Trátalos como puntos de partida para adaptar — vuelve a verificar los nombres de campos y la forma de las fibras contra las versiones instaladas del proyecto antes de confiar en cualquier resultado. Son pistas, no APIs estables garantizadas.
Recorre el árbol de fibras desde un nodo DOM para encontrar hooks/observadores:
// Selecciona un elemento DOM renderizado por el componente afectado (p.ej., a través de $0 en las herramientas de desarrollador).
const el = $0; // o document.querySelector('[data-testid="..."]')
const key = Object.keys(el).find(k => k.startsWith('__reactFiber$'));
let fiber = el[key];
// Recorre el árbol de fibras hacia arriba/abajo; memoizedState es una lista enlazada de hooks.
// react-query almacena su observador en el memoizedState de un hook.
function* fibers(root) {
const stack = [root];
while (stack.length) {
const f = stack.pop();
if (!f) continue;
yield f;
if (f.child) stack.push(f.child);
if (f.sibling) stack.push(f.sibling);
}
}
// Escanea el estado del hook en busca de algo que parezca un QueryObserver.
function findObservers(fiber) {
const found = [];
for (const f of fibers(fiber)) {
let hook = f.memoizedState;
while (hook) {
const s = hook.memoizedState;
if (s && typeof s === 'object') {
// Las instancias de QueryObserver exponen getCurrentResult()/getCurrentQuery().
if (typeof s.getCurrentResult === 'function') found.push(s);
// Algunas versiones lo anidan; también verifica envoltorios comunes.
if (s.observer && typeof s.observer.getCurrentResult === 'function') found.push(s.observer);
}
hook = hook.next;
}
}
return found;
}
const observers = findObservers(fiber);
Lee el estado de la consulta en vivo de un observador:
const obs = observers[0];
const r = obs.getCurrentResult();
console.log({
status: r.status, // 'pending' | 'error' | 'success'
fetchStatus: r.fetchStatus, // 'fetching' | 'paused' | 'idle'
dataUpdateCount: obs.getCurrentQuery().state.dataUpdateCount,
error: r.error,
});
// pending + fetching + dataUpdateCount 0 ==> abandonado a mitad de vuelo, no un error de petición.
Fuerza un refetch para demostrar que el queryFn es saludable:
obs.refetch().then(r => console.log('refetch:', r.status, r.data)).catch(e => console.log('refetch falló:', e));
// Se resuelve instantánea y correctamente ==> queryFn/cliente/esquema están bien;
// la consulta original fue eliminada corriente arriba.
Si el proyecto expone el QueryClient (p.ej., en window para depuración, o accesible desde una fibra de proveedor), prefiere queryClient.getQueryCache().findAll() e inspecciona el state y observers de cada consulta directamente — es más robusto que recorrer fibras, cuando esté disponible.
Finalización
Cuando hayas localizado el error:
- Indica en qué lado de la bifurcación cayó: "ruta de datos rota" o "consulta abandonada".
- Si está abandonada, nombra la acción de ciclo de vida/caché corriente arriba que la eliminó.
- Solo entonces relee el archivo fuente específico responsable — ahora sabes cuál es.