DevToolsManager
class DevToolsManager implements Manager
Se integra con Redux DevTools para hacer seguimiento del estado y las acciones. Nota: no se integra con el viaje en el tiempo.
Agrega la extensión de Chrome o la extensión de Firefox a tu navegador para empezar.
DevToolsManager implementa Manager
constructor(options?, skipLogging?)
options
Argumentos que se envían a redux devtools.
Por ejemplo, podemos habilitar la opción trace para ayudar a rastrear desde dónde se despachan las acciones.
- Web
- React Native
- NextJS
- Expo
import { DataProvider, getDefaultManagers } from '@data-client/react';
import { createRoot } from 'react-dom/client';
import App from './App';
const managers = getDefaultManagers({
devToolsManager: { trace: true },
});
createRoot(document.body).render(
<DataProvider managers={managers}>
<App />
</DataProvider>,
);
import { DataProvider, getDefaultManagers } from '@data-client/react';
import { AppRegistry } from 'react-native';
import App from './App';
const managers = getDefaultManagers({
devToolsManager: { trace: true },
});
const Root = () => (
<DataProvider managers={managers}>
<App />
</DataProvider>
);
AppRegistry.registerComponent('MyApp', () => Root);
'use client';
import { getDefaultManagers } from '@data-client/react';
import { DataProvider } from '@data-client/react/nextjs';
const managers = getDefaultManagers({
devToolsManager: { trace: true },
});
export default function Provider({
children,
}: {
children: React.ReactNode;
}) {
return <DataProvider managers={managers}>{children}</DataProvider>;
}
import Provider from './Provider';
export default function RootLayout({ children }) {
return (
<html>
<body>
<Provider>{children}</Provider>
</body>
</html>
);
}
import { Stack } from 'expo-router';
import { DataProvider, getDefaultManagers } from '@data-client/react';
const managers = getDefaultManagers({
devToolsManager: { trace: true },
});
export default function RootLayout() {
return (
<DataProvider managers={managers}>
<Stack>
<Stack.Screen name="index" />
</Stack>
</DataProvider>
);
}
skipLogging
(action: ActionTypes) => boolean
Permite omitir algunas acciones para que no se registren en las devtools del navegador.
Por defecto omite las acciones fetch en curso
- Web
- React Native
- NextJS
- Expo
import {
DataProvider,
DevToolsManager,
getDefaultManagers,
} from '@data-client/react';
import { createRoot } from 'react-dom/client';
import App from './App';
// production builds leave out DevToolsManager
const managers = getDefaultManagers({
devToolsManager: new DevToolsManager(undefined, () => true),
});
createRoot(document.body).render(
<DataProvider managers={managers}>
<App />
</DataProvider>,
);
import {
DataProvider,
DevToolsManager,
getDefaultManagers,
} from '@data-client/react';
import { AppRegistry } from 'react-native';
import App from './App';
// production builds leave out DevToolsManager
const managers = getDefaultManagers({
devToolsManager: new DevToolsManager(undefined, () => true),
});
const Root = () => (
<DataProvider managers={managers}>
<App />
</DataProvider>
);
AppRegistry.registerComponent('MyApp', () => Root);
'use client';
import { DevToolsManager, getDefaultManagers } from '@data-client/react';
import { DataProvider } from '@data-client/react/nextjs';
// production builds leave out DevToolsManager
const managers = getDefaultManagers({
devToolsManager: new DevToolsManager(undefined, () => true),
});
export default function Provider({
children,
}: {
children: React.ReactNode;
}) {
return <DataProvider managers={managers}>{children}</DataProvider>;
}
import Provider from './Provider';
export default function RootLayout({ children }) {
return (
<html>
<body>
<Provider>{children}</Provider>
</body>
</html>
);
}
import { Stack } from 'expo-router';
import {
DataProvider,
DevToolsManager,
getDefaultManagers,
} from '@data-client/react';
// production builds leave out DevToolsManager
const managers = getDefaultManagers({
devToolsManager: new DevToolsManager(undefined, () => true),
});
export default function RootLayout() {
return (
<DataProvider managers={managers}>
<Stack>
<Stack.Screen name="index" />
</Stack>
</DataProvider>
);
}
Omitir actualizaciones de alta frecuencia
Al usar WebSockets u otras fuentes de datos en tiempo real,
las actualizaciones de alta frecuencia pueden saturar la extensión DevTools. Usa la opción predicate para
filtrar tipos de acciones o schemas específicos:
import { getDefaultManagers, actionTypes } from '@data-client/react';
import { Ticker } from './resources/Ticker';
const managers = getDefaultManagers({
devToolsManager: {
// Increase latency buffer for high-frequency updates
latency: 1000,
// Skip WebSocket SET actions for Ticker to reduce log spam
// (including batched set([Ticker], rows) writes)
predicate: (state, action) =>
action.type !== actionTypes.SET ||
(action.schema !== Ticker && action.schema[0] !== Ticker),
},
});
Acceso programático al store
En modo de desarrollo, DevToolsManager registra cada Controller en
globalThis.__DC_CONTROLLERS__, un Map cuya clave es el nombre de la conexión de devtools. También registra las
acciones despachadas más recientes de cada uno en globalThis.__DC_ACTIONS__, bajo la misma clave. Esto funciona
en navegadores, React Native y Node, con o sin la extensión Redux DevTools.
// List all registered providers
__DC_CONTROLLERS__.keys();
// Get state from the first provider
__DC_CONTROLLERS__.values().next().value.getState();
// Get state by name
__DC_CONTROLLERS__.get('Data Client: My App').getState();
// Last 10 actions, including the ones dispatched while the page loaded
__DC_ACTIONS__.get('Data Client: My App').slice(-10);
Esto es útil para que los asistentes de programación con IA que usan Chrome DevTools MCP
o Expo MCP inspeccionen e interactúen programáticamente
con el store. Cada DataProvider se registra de forma independiente, por lo que
se admiten varios providers en la misma página. Cuando un nombre ya está en uso, el siguiente
se registra como 'Data Client: My App (2)', y así sucesivamente; define name para dar a cada uno una clave reconocible. Donde no hay window, como en
el renderizado en el servidor, un store reemplaza al anterior con el mismo nombre, ya que los stores por petición nunca se limpian.
El registro de acciones conserva hasta el doble de maxAge
acciones (100 por defecto) y omite los mismos fetches duplicados que omite la extensión. Para mantener bajo su uso de memoria, solo las
20 acciones más recientes conservan sus args y response; las más antiguas conservan type, key, endpoint, meta y las respuestas
de error. Mientras un endpoint hace polling, el registro conserva solo sus últimas
maxPollingActions acciones por cada clave (endpoint y args) (4 por defecto, los dos últimos sondeos), para que el polling no desplace todo lo demás:
- Web
- React Native
- NextJS
- Expo
import { DataProvider, getDefaultManagers } from '@data-client/react';
import { createRoot } from 'react-dom/client';
import App from './App';
const managers = getDefaultManagers({
devToolsManager: { maxPollingActions: 10 },
});
createRoot(document.body).render(
<DataProvider managers={managers}>
<App />
</DataProvider>,
);
import { DataProvider, getDefaultManagers } from '@data-client/react';
import { AppRegistry } from 'react-native';
import App from './App';
const managers = getDefaultManagers({
devToolsManager: { maxPollingActions: 10 },
});
const Root = () => (
<DataProvider managers={managers}>
<App />
</DataProvider>
);
AppRegistry.registerComponent('MyApp', () => Root);
'use client';
import { getDefaultManagers } from '@data-client/react';
import { DataProvider } from '@data-client/react/nextjs';
const managers = getDefaultManagers({
devToolsManager: { maxPollingActions: 10 },
});
export default function Provider({
children,
}: {
children: React.ReactNode;
}) {
return <DataProvider managers={managers}>{children}</DataProvider>;
}
import Provider from './Provider';
export default function RootLayout({ children }) {
return (
<html>
<body>
<Provider>{children}</Provider>
</body>
</html>
);
}
import { Stack } from 'expo-router';
import { DataProvider, getDefaultManagers } from '@data-client/react';
const managers = getDefaultManagers({
devToolsManager: { maxPollingActions: 10 },
});
export default function RootLayout() {
return (
<DataProvider managers={managers}>
<Stack>
<Stack.Screen name="index" />
</Stack>
</DataProvider>
);
}
Los controllers y sus registros se eliminan cuando se llama a cleanup(). Nada de esto existe en las compilaciones de producción.
Más información
Usar este Manager permite depurar e inspeccionar el store en el navegador.