createStore
The irreducible floor. A value and three methods.
import { createStore } from '@kin-store/core';Basic usage
type TodoState = { todos: string[]; status: 'idle' | 'loading' | 'failed' };
const store = createStore({ todos: [], status: 'idle' } as TodoState);createStore holds any value and returns an object with three methods: getState, setState, and subscribe. Logic lives in plain top-level functions — no dispatch, no action types.
function addTodo(text: string): void {
store.setState((s) => ({ ...s, todos: [...s.todos, text] }));
}
async function fetchTodos(): Promise<void> {
store.setState((s) => ({ ...s, status: 'loading' }));
try {
const todos = await api.getTodos();
store.setState({ todos, status: 'idle' });
} catch {
store.setState((s) => ({ ...s, status: 'failed' }));
}
}
addTodo('Hello world');
console.log(store.getState()); // { todos: ['Hello world'], status: 'idle' }API
getState()
Reads the current state synchronously. Always returns the latest value.
const { todos } = store.getState();setState(nextState)
Accepts a new value or an updater function. Notifies all subscribers.
// Replace the whole state.
store.setState({ todos: [], status: 'idle' });
// Merge via updater (the idiomatic pattern — avoids stale closures).
store.setState((s) => ({ ...s, todos: [...s.todos, 'new item'] }));subscribe(listener)
Fires on every state change. Returns an unsubscribe function.
const unsubscribe = store.subscribe((getState, prevState) => {
console.log(prevState, '->', getState());
});
// Stop listening.
unsubscribe();The listener receives getState (a getter, not the value itself) and prevState (the state before the change). Using a getter prevents you from accidentally closing over a stale snapshot.
listenerWithSelector
Wraps a listener so it only fires when a selected slice of the state changes. Useful for subscribing to a store outside of React without unnecessary re-runs.
import { listenerWithSelector } from '@kin-store/core';
const store = createStore({ count: 0, name: 'Alice' });
store.subscribe(
listenerWithSelector(
(getSlice, prevSlice) => console.log('count:', prevSlice, '->', getSlice()),
(state) => state.count,
),
);
store.setState({ count: 1, name: 'Alice' }); // logs: count: 0 -> 1
store.setState({ count: 1, name: 'Bob' }); // no log — count didn't changeWhen to use createStore
createStore is the right choice when:
- You want the minimal API with no overhead
- Logic is small enough to live in module-level functions
- You're building a library or utility on top of kin-store
When you need methods colocated with the store, a dispatch pipeline, or middleware — reach for withPlugins.