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: get, set, and subscribe. Logic lives in plain top-level functions — no dispatch, no action types.
function addTodo(text: string): void {
store.set((s) => ({ ...s, todos: [...s.todos, text] }));
}
async function fetchTodos(): Promise<void> {
store.set((s) => ({ ...s, status: "loading" }));
try {
const todos = await api.getTodos();
store.set({ todos, status: "idle" });
} catch {
store.set((s) => ({ ...s, status: "failed" }));
}
}
addTodo("Hello world");
console.log(store.get()); // { todos: ['Hello world'], status: 'idle' }API
get()
Reads the current state synchronously. Always returns the latest value.
const { todos } = store.get();set(nextState)
Accepts a new value or an updater function. Notifies all subscribers.
// Replace the whole state.
store.set({ todos: [], status: "idle" });
// Merge via updater (the idiomatic pattern — avoids stale closures).
store.set((s) => ({ ...s, todos: [...s.todos, "new item"] }));subscribe(listener)
Fires on every state change. Returns an unsubscribe function.
const unsubscribe = store.subscribe((get, prevState) => {
console.log(prevState, "->", get());
});
// Stop listening.
unsubscribe();The listener receives get (a getter, not the value itself) and prevState (the state before the change). Using a getter allows derive to stay lazy: a derived store defers recomputation until something actually calls its get(), instead of recomputing eagerly on every upstream change.
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.set({ count: 1, name: "Alice" }); // logs: count: 0 -> 1
store.set({ 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.