ilokesto

React

Use the React adapter when a React component needs a subscribed value from an @ilokesto/store-backed state container. Import from @ilokesto/state/react, not from the root package.

Plain state hook

Plain state returns the same shape as a small React state hook: [selection, setState].

import { create } from '@ilokesto/state/react';

type CounterState = { count: number; label: string };

const useCounter = create<CounterState>({ count: 0, label: 'Clicks' });

export function CounterButton() {
  const [count, setCounter] = useCounter((state) => state.count);
  const [label] = useCounter((state) => state.label);

  return (
    <button onClick={() => setCounter((prev) => ({ ...prev, count: prev.count + 1 }))}>
      {label}: {count}
    </button>
  );
}

The selector decides the value returned as the first tuple item. The writer still updates the full state, so keep object updates immutable.

Reducer hook

Reducer state uses the same create(reduce, initialState) overload. The tuple writer becomes dispatch.

import { create } from '@ilokesto/state/react';

type Action = { type: 'increment' } | { type: 'reset' };

const reduce = (state: { count: number }, action: Action) => {
  if (action.type === 'increment') return { count: state.count + 1 };
  if (action.type === 'reset') return { count: 0 };
  return state;
};

const useCounter = create(reduce, { count: 0 });

function ReducerCounter() {
  const [count, dispatch] = useCounter((state) => state.count);
  return <button onClick={() => dispatch({ type: 'increment' })}>{count}</button>;
}

Outside React lifecycle

readOnly() and writeOnly() are attached to the hook function. Use them in modules, service callbacks, tests, or event buses that should not call a hook.

const readCount = useCounter.readOnly((state) => state.count);
const dispatch = useCounter.writeOnly();
dispatch({ type: 'reset' });

Caveats

  • The returned function is a React hook because it uses useSyncExternalStore; call it only inside components or custom hooks.
  • getServerSnapshot starts from the store initial state, so SSR hydration should use the same initial state on server and client.
  • Selectors should be pure and cheap. React memoizes snapshots, but expensive selectors still run during render/snapshot work.

On this page