一键导入
tanstack-store
Framework-agnostic, immutable reactive data store with framework adapters for React, Vue, Solid, Angular, and Svelte.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Framework-agnostic, immutable reactive data store with framework adapters for React, Vue, Solid, Angular, and Svelte.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Design engineering principles for making interfaces feel polished. Use when building UI components, reviewing frontend code, implementing animations, hover states, shadows, borders, typography, micro-interactions, enter/exit animations, or any visual detail work. Triggers on UI polish, design details, "make it feel better", "feels off", stagger animations, border radius, optical alignment, font smoothing, tabular numbers, image outlines, box shadows.
Project scaffolding CLI with 30+ integrations, custom templates, and MCP server for AI agents.
Opinionated toolkit for building, versioning, and publishing high-quality JavaScript/TypeScript packages.
Reactive client-first store for your API with collections, live queries, and optimistic mutations.
Centralized, extensible devtools panel for TanStack libraries with a plugin architecture.
Framework-agnostic debouncing, throttling, rate limiting, queuing, and batching utilities.
| name | tanstack-store |
| description | Framework-agnostic, immutable reactive data store with framework adapters for React, Vue, Solid, Angular, and Svelte. |
TanStack Store is a lightweight reactive store (signals-like) that powers the internals of TanStack libraries. It provides Store for state, Derived for computed values, Effect for side effects, and batch for atomic updates. Framework adapters provide reactive hooks.
Core: @tanstack/store
React: @tanstack/react-store
npm install @tanstack/store @tanstack/react-store
import { Store } from "@tanstack/store";
const countStore = new Store(0);
const userStore = new Store<{ name: string; email: string }>({
name: "Alice",
email: "alice@example.com",
});
// Function updater (immutable update)
countStore.setState((prev) => prev + 1);
userStore.setState((prev) => ({ ...prev, name: "Bob" }));
const unsub = countStore.subscribe(() => {
console.log("Count:", countStore.state);
});
// Cleanup
unsub();
const store = new Store(initialState, {
// Custom update function
updateFn: (prevValue) => (updater) => {
return updater(prevValue); // custom logic
},
// Callback on subscribe
onSubscribe: (listener, store) => {
console.log("New subscriber");
return () => console.log("Unsubscribed");
},
// Callback on every update
onUpdate: () => {
console.log("State updated:", store.state);
},
});
store.state; // Current state
store.prevState; // Previous state
store.listeners; // Set of listener callbacks
import { Store, Derived } from "@tanstack/store";
const count = new Store(5);
const multiplier = new Store(2);
const doubled = new Derived({
deps: [count, multiplier],
fn: ({ currDepVals }) => currDepVals[0] * currDepVals[1],
});
// MUST mount to activate
const unmount = doubled.mount();
console.log(doubled.state); // 10
count.setState(() => 10);
console.log(doubled.state); // 20
// Cleanup
unmount();
const accumulated = new Derived({
deps: [count],
fn: ({ prevVal, currDepVals }) => {
return currDepVals[0] + (prevVal ?? 0);
},
});
const filtered = new Derived({
deps: [dataStore, filterStore],
fn: ({ currDepVals }) => currDepVals[0].filter(matchesFilter(currDepVals[1])),
});
const sorted = new Derived({
deps: [filtered, sortStore],
fn: ({ currDepVals }) => [...currDepVals[0]].sort(comparator(currDepVals[1])),
});
const paginated = new Derived({
deps: [sorted, pageStore],
fn: ({ currDepVals }) =>
currDepVals[0].slice(currDepVals[1].offset, currDepVals[1].offset + currDepVals[1].limit),
});
import { Store, Effect } from "@tanstack/store";
const count = new Store(0);
const logger = new Effect({
deps: [count],
fn: () => {
console.log("Count changed:", count.state);
// Optionally return cleanup function
return () => console.log("Cleaning up");
},
eager: false, // true = run immediately on mount
});
const unmount = logger.mount();
count.setState(() => 1); // logs: "Count changed: 1"
unmount();
const timerEffect = new Effect({
deps: [intervalStore],
fn: () => {
const id = setInterval(() => {
/* ... */
}, intervalStore.state);
return () => clearInterval(id); // cleanup on next run or unmount
},
});
Group multiple updates into one notification:
import { batch } from "@tanstack/store";
// Subscribers fire only once with final state
batch(() => {
countStore.setState(() => 1);
nameStore.setState(() => "Alice");
settingsStore.setState((prev) => ({ ...prev, theme: "dark" }));
});
import { useStore } from "@tanstack/react-store";
// Subscribe to full state
function Counter() {
const count = useStore(countStore);
return <button onClick={() => countStore.setState((c) => c + 1)}>{count}</button>;
}
// Subscribe with selector (performance optimization)
function UserName() {
const name = useStore(userStore, (state) => state.name);
return <span>{name}</span>;
}
// Subscribe to Derived
function DoubledDisplay() {
const value = useStore(doubledDerived);
return <span>{value}</span>;
}
Prevents re-renders when selector returns structurally-equal objects:
import { useStore } from "@tanstack/react-store";
import { shallow } from "@tanstack/react-store";
function TodoList() {
// Without shallow: re-renders on ANY state change (new object ref)
// With shallow: only re-renders when items actually change
const items = useStore(todosStore, (state) => state.items, shallow);
return <ul>{items.map(/* ... */)}</ul>;
}
function MyComponent() {
useEffect(() => {
const unmountDerived = myDerived.mount();
const unmountEffect = myEffect.mount();
return () => {
unmountDerived();
unmountEffect();
};
}, []);
const value = useStore(myDerived);
return <span>{value}</span>;
}
// stores/counter.ts
import { Store, Derived } from "@tanstack/store";
export const counterStore = new Store(0);
export const doubledCount = new Derived({
deps: [counterStore],
fn: ({ currDepVals }) => currDepVals[0] * 2,
});
// Actions as plain functions
export function increment() {
counterStore.setState((c) => c + 1);
}
export function reset() {
counterStore.setState(() => 0);
}
| Framework | Package | Hook/Composable |
|---|---|---|
| React | @tanstack/react-store | useStore(store, selector?, equalityFn?) |
| Vue | @tanstack/vue-store | useStore(store, selector?) (returns computed ref) |
| Solid | @tanstack/solid-store | useStore(store, selector?) (returns signal) |
| Angular | @tanstack/angular-store | injectStore(store, selector?) (returns signal) |
| Svelte | @tanstack/svelte-store | useStore(store, selector?) (returns $state) |
useStore to prevent unnecessary re-rendersshallow when selectors return objects/arraysmount() on Derived and Effect instancessetStatebatch for multiple related updatesfn for timers/listenersmount() Derived/Effect (they won't activate)store.state directly instead of using setStateshallowuseStore without a selector (subscribes to everything)eager: true when Effect should run immediately