createStore
A value and six methods:
get,set,merge,subscribefor a minimal startuse,destroyfor opt-in structure and teardown
import { createStore } from "@kintools/store-core";
type TodoState = { todos: string[]; status: "idle" | "loading" | "failed" };
const store = createStore({ todos: [], status: "idle" } as TodoState);createStore holds any value. Logic can live in plain top-level functions with
no methods registered at all, or colocated on the store with .use(); see
Structure.
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" });
// Update via a function (avoids stale closures).
store.set((s) => ({ ...s, todos: [...s.todos, "new item"] }));merge(partial)
Shallow-merges a partial object into plain-object state.
Behaves like set((s) => ({ ...s, ...partial })), or
set((s) => ({ ...s, ...partial(s) })) when given a function.
Plain-object state always notifies because the merge creates a new object. For
other state values, partial replaces the current state, and subscribers are
notified only when the two values are not the same by Object.is.
const store = createStore({ count: 0, name: "a" });
store.merge({ count: 1 });
console.log(store.get()); // { count: 1, name: "a" }
store.merge((s) => ({ count: s.count + 1 }));
console.log(store.get()); // { count: 2, name: "a" }subscribe(listener)
Registers a listener that is called on every state change. Returns a function that unregisters it.
this inside a regular-function listener is bound to the store, so it can read
this.get() without a separate closure reference. An arrow-function listener
doesn't get this rebinding, but can read the store from its enclosing closure
instead.
const unsubscribe = store.subscribe(function (prevState) {
console.log(prevState, "->", this.get());
});
unsubscribe(); // stop listeningdestroy()
Removes all listeners and runs every registered onDestroy hook. Safe to call
more than once; any other method throws after the first call.
store.destroy();
store.get(); // throws: "The store has been destroyed"listenerWithSelector
Wraps a listener so it only fires when a selected value from the state changes.
Useful for subscribing to a store outside of React without unnecessary re-runs.
The listener receives the previous selected value, and this inside it is the
store. Values are compared with shallowEqual by default; pass { equal } to
override.
import { listenerWithSelector } from "@kintools/store-core";
const store = createStore({ count: 0, name: "Alice" });
const selectCount = (state: { count: number }) => state.count;
store.subscribe(
listenerWithSelector(
function (prevSelected, nextSelected) {
console.log("count:", prevSelected, "->", nextSelected);
},
selectCount,
),
);
store.set({ count: 1, name: "Alice" }); // logs: count: 0 -> 1
store.set({ count: 1, name: "Bob" }); // no log, count didn't changeWhen to keep it minimal
A bare createStore, no .use() calls, 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 want methods colocated with the store, namespacing, or lifecycle hooks,
reach for .use(); see Structure.