Skip to main content

DevToolsManager

class DevToolsManager implements Manager

Integrates with Redux DevTools to track state and actions. Note: does not integrate time-travel.

Add the chrome extension or firefox extension to your browser to get started.

implements

DevToolsManager implements Manager

constructor(options?, skipLogging?)​

options​

Arguments to send to redux devtools.

For example, we can enable the trace option to help track down where actions are dispatched from.

index.tsx
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>,
);

skipLogging​

(action: ActionTypes) => boolean

Can skip some actions to be registered in the browser devtool.

By default will skip inflight fetch actions

index.tsx
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>,
);

Skipping high-frequency updates​

When using WebSockets or other real-time data sources, high-frequency updates can overwhelm the DevTools extension. Use the predicate option to filter out specific action types or schemas:

managers.ts
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),
},
});

Programmatic store access​

In development mode, DevToolsManager registers each Controller on globalThis.__DC_CONTROLLERS__, a Map keyed by the devtools connection name. It also records the most recent dispatched actions for each one on globalThis.__DC_ACTIONS__, under the same key. This works in browsers, React Native, and Node, with or without the Redux DevTools extension.

Browser DevTools console
// 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);

This is useful for AI coding assistants using the Chrome DevTools MCP or Expo MCP to programmatically inspect and interact with the store. Each DataProvider registers independently, so multiple providers on the same page are supported. When a name is already taken, the next one registers as 'Data Client: My App (2)', and so on; set name to give each a recognizable key. Where there is no window, as when server rendering, a store replaces the previous one with its name instead, since per-request stores are never cleaned up.

The action log keeps up to twice maxAge actions (100 by default) and skips the same duplicate fetches the extension does. To keep its memory small, only the newest 20 actions keep their args and response; older ones keep type, key, endpoint, meta and error responses. While an endpoint polls, the log keeps only its last maxPollingActions actions for each key (endpoint and args) (4 by default, the last two polls), so polling doesn't push everything else out:

index.tsx
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>,
);

Controllers and their logs are removed when cleanup() is called. None of this exists in production builds.

More info​

Using this Manager allows in browser debugging and store inspection.