redux-devtools

Every committed change to a named atom is sent as an action; the extension's time-travel / commit / reset controls restore state back onto the store.

Requires the extension
Install the Redux DevTools browser extension. Without it, connectReduxDevtools warns once and returns a no-op handle.

Install

bun add @valdres/redux-devtools

Usage

Call connectReduxDevtools(store) once on mount; call disconnect() on teardown.

import { useEffect } from "react"
import { useStore } from "valdres-react"
import { connectReduxDevtools } from "@valdres/redux-devtools"

function DevtoolsBridge() {
    const store = useStore()
    useEffect(() => connectReduxDevtools(store).disconnect, [store])
    return null
}

Vanilla: pass the store instance directly.

import { store } from "valdres"
import { connectReduxDevtools } from "@valdres/redux-devtools"

const handle = connectReduxDevtools(store())
// handle.disconnect() to detach

Options

connectReduxDevtools(store, options?) accepts:

OptionTypeDefaultNotes
namestring"valdres"Instance name in the extension dropdown.
serialize(state, value) => unknownidentityShape values that aren't structured-cloneable before they're sent.
scopesbooleantrueInclude scope state under @scopes and react to scope writes.
unnamed"track" | "ignore""track"track labels unnamed atoms unnamed_atom_N; ignore omits them.
selectorsbooleanfalseAlso report named selectors under @computed (display-only, never restored).

Exports

ExportKindType
connectReduxDevtoolsutil fn(store: Store, options?: ConnectReduxDevtoolsOptions) => ReduxDevtoolsHandle
ConnectReduxDevtoolsOptionstype{ name?, serialize?, scopes?, unnamed?, selectors? }
ReduxDevtoolsHandletype{ disconnect(): void }
DevtoolsSnapshottypeRecord<string, unknown> & { "@scopes"?, "@computed"? }
ReduxDevtoolsConnectiontypeextension connection surface
ReduxDevtoolsExtensiontypewindow.__REDUX_DEVTOOLS_EXTENSION__ surface
ReduxDevtoolsMessagetypemessage from the extension

Notes

  • A named transaction (store.txn(fn, name)) arrives as a single action.
  • Default (non-enumerable) stores seed lazily as state first changes; only store(id, { enumerable: true }) is seeded up front.
  • Only how you obtain the Store differs per framework (useStore, injectStore, getValdresContext, or the bare instance).