Migrating from hox

Kerros has a familiar createStore API, with two important differences: a Kerros Store always has an explicit Provider, and every read requires a selector.

Migrate a scoped Store

A regular hox Store:

import { createStore } from 'hox'

export const [useCounter, CounterProvider] = createStore(() => {
  const [count, setCount] = useState(0)
  return { count, setCount }
})

The definition stays almost identical; replace the import:

import { createStore } from '@violetflux/kerros'

function useCounterStoreValue() {
  const [count, setCount] = useState(0)
  return { count, setCount }
}

export const [useCounter, CounterProvider] = createStore(useCounterStoreValue)

Replace whole-Store reads:

const { count, setCount } = useCounter()

with explicit selectors:

const { count, setCount } = useCounter(s => ({
  count: s.count,
  setCount: s.setCount,
}))

Migrate a global Store

hox can register a hidden global Store with createGlobalStore and HoxRoot:

import { createGlobalStore } from 'hox'

export const [useAccount, getAccount] = createGlobalStore(() => {
  const [user, setUser] = useState<User | null>(null)
  return { user, setUser }
})

Kerros does not provide hidden global Stores. Replace it with createStore:

import { createStore } from '@violetflux/kerros'

function useAccountStoreValue() {
  const [user, setUser] = useState<User | null>(null)
  return { user, setUser }
}

export const [useAccount, AccountProvider] = createStore(useAccountStoreValue)

Then mount the Provider at the application root:

createRoot(document.getElementById('root')!).render(
  <AccountProvider>
    <App />
  </AccountProvider>,
)

Mount several root Stores in dependency order:

<AccountProvider>
  <TaskProvider>
    <App />
  </TaskProvider>
</AccountProvider>

Replace getXxxStore

Kerros does not expose a static read such as getAccount(). Components and dependent Stores use selectors:

const { user } = useAccount(s => ({ user: s.user }))

For logging, request interceptors, or other non-React code, pass the required value as an argument or move that state into a genuinely React-independent external Store. Avoid maintaining a second mirrored copy just for static reads.

Split a large global Store

You do not have to move every field into one new root Store. Split by responsibility and mount dependencies in order:

Account → Task → Editor

High-frequency local state, such as drafts and dialog flags, can live in Providers closer to the feature that owns it.

useMemoizedFn and useEffectEvent

Public Store actions can be ordinary functions. React Compiler may stabilize values it can prove safe; without Compiler, follow normal React function-identity rules.

React 19's useEffectEvent is only for events called from Effects. Do not use it to wrap button handlers, submit actions, or other public Store methods.

After migrating, search the repository for remaining references to hox, HoxRoot, createGlobalStore, static getXxxStore calls, and useMemoizedFn.