# DevToolsManager

```typescript
class DevToolsManager implements Manager
```

Integrates with [Redux DevTools](https://github.com/reduxjs/redux-devtools) to track
state and [actions](https://dataclient.io/docs/api/Actions.md). Note: does not integrate time-travel.

Add the [chrome extension](https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd?hl=en)
or [firefox extension](https://addons.mozilla.org/en-US/firefox/addon/reduxdevtools/) to your
browser to get started.

> **Info: implements**
>
> `DevToolsManager` implements [Manager](https://dataclient.io/docs/api/Manager.md)

## constructor(options?, skipLogging?)

### options

[Arguments](https://github.com/reduxjs/redux-devtools/blob/main/extension/docs/API/Arguments.md)
to send to redux devtools.

For example, we can enable the [trace](https://github.com/reduxjs/redux-devtools/blob/main/extension/docs/API/Arguments.md#trace) option to help track down where actions are dispatched from.

**Web**

```tsx title="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>,
);
```

**React Native**

```tsx title="index.tsx"
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);
```

**NextJS**

```tsx title="app/Provider.tsx"
'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>;
}
```

```tsx title="app/layout.tsx"
import Provider from './Provider';

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <Provider>{children}</Provider>
      </body>
    </html>
  );
}
```

**Expo**

```tsx title="app/_layout.tsx"
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`

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

By default will skip inflight [fetch actions](https://dataclient.io/docs/api/Controller.md#fetch)

**Web**

```tsx title="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>,
);
```

**React Native**

```tsx title="index.tsx"
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);
```

**NextJS**

```tsx title="app/Provider.tsx"
'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>;
}
```

```tsx title="app/layout.tsx"
import Provider from './Provider';

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <Provider>{children}</Provider>
      </body>
    </html>
  );
}
```

**Expo**

```tsx title="app/_layout.tsx"
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>
  );
}
```

#### Skipping high-frequency updates

When using [WebSockets](https://dataclient.io/docs/concepts/managers.md#data-stream) 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:

```ts title="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 {#controllers}

In development mode, `DevToolsManager` registers each [Controller](https://dataclient.io/docs/api/Controller.md) on
`globalThis.__DC_CONTROLLERS__` — a `Map` keyed by the devtools connection name. This works
in browsers, React Native, and Node.

```js title="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();
```

This is useful for AI coding assistants using the [Chrome DevTools MCP](https://developer.chrome.com/blog/chrome-devtools-mcp)
or [Expo MCP](https://docs.expo.dev/eas/ai/mcp/) to programmatically inspect and interact
with the store. Each [DataProvider](https://dataclient.io/docs/api/DataProvider.md) registers independently, so
multiple providers on the same page are fully supported.

Controllers are removed from the map when `cleanup()` is called.

## More info

Using this Manager allows in browser [debugging and store inspection](https://dataclient.io/docs/getting-started/debugging.md).
