关于 tanstack-store
TanStack Store 是面向使用 TanStack 库或受支持框架适配器的开发者的响应式状态库。它提供状态存储、计算值、副作用和批量更新。
主要功能
- 01响应式状态存储
- 02使用 Derived 计算值
- 03支持清理的副作用
- 04批量更新
- 05框架适配器和 React hooks
适用场景
- 01管理应用共享状态
- 02派生筛选或排序后的数据
- 03在状态变化时运行副作用
- 04合并相关更新的通知
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ tanstack-skills/ tanstack-skills --skill tanstack-store安装后在 Claude Code 中输入/tanstack-store确认已加载。
详细介绍
概述
TanStack Store 是一个轻量级的响应式存储(类似信号),为 TanStack 库的内部功能提供支持。它提供用于状态管理的 Store、用于计算值的 Derived、用于副作用的 Effect,以及用于原子更新的 batch。框架适配器提供响应式钩子。
核心: @tanstack/store
React: @tanstack/react-store
安装
npm install @tanstack/store @tanstack/react-store
Store
创建 Store
import { Store } from '@tanstack/store'
const countStore = new Store(0)
const userStore = new Store<{ name: string; email: string }>({
name: 'Alice',
email: '[email protected]',
})
更新状态
// 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()
Store 选项
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 属性
store.state // Current state
store.prevState // Previous state
store.listeners // Set of listener callbacks
Derived(计算值)
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()
使用先前值的 Derived
const accumulated = new Derived({
deps: [count],
fn: ({ prevVal, currDepVals }) => {
return currDepVals[0] + (prevVal ?? 0)
},
})
链式 Derived
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,
),
})
Effect(副作用)
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()
带清理函数的 Effect
const timerEffect = new Effect({
deps: [intervalStore],
fn: () => {
const id = setInterval(() => { /* ... */ }, intervalStore.state)
return () => clearInterval(id) // cleanup on next run or unmount
},
})
Batch
将多次更新合并为一次通知:
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' }))
})
React 集成
useStore 钩子
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>
}
浅比较函数
当选择器返回结构相等的对象时,避免重新渲染:
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>
}
在 React 中挂载 Derived/Effect
function MyComponent() {
useEffect(() => {
const unmountDerived = myDerived.mount()
const unmountEffect = myEffect.mount()
return () => {
unmountDerived()
unmountEffect()
}
}, [])
const value = useStore(myDerived)
return <span>{value}</span>
}
模块级 Store 模式
// 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)
}
框架适配器
| 框架 | 包 | 钩子/组合式函数 |
|---|---|---|
| React | @tanstack/react-store | useStore(store, selector?, equalityFn?) |
| Vue | @tanstack/vue-store | useStore(store, selector?)(返回计算属性 ref) |
| Solid | @tanstack/solid-store | useStore(store, selector?)(返回信号) |
| Angular | @tanstack/angular-store | injectStore(store, selector?)(返回信号) |
| Svelte | @tanstack/svelte-store | useStore(store, selector?)(返回 $state) |
最佳实践
- 在模块级别定义 Store - 它们是单例
- 在
useStore中使用选择器,避免不必要的重新渲染 - 当选择器返回对象/数组时使用
shallow - 始终对 Derived 和 Effect 实例调用
mount() - 始终清理卸载函数(尤其是在 React
useEffect中) - 切勿直接修改状态 - 始终使用
setState - 多次相关更新时使用
batch - 使用 Derived 链进行数据转换(筛选 -> 排序 -> 分页)
- 在 Effect 的
fn中返回清理函数,用于计时器/监听器 - 尽可能选择基本类型(无需 equality fn)
常见问题
- 忘记对 Derived/Effect 调用
mount()(它们不会激活) - 未清理订阅/卸载函数(会造成内存泄漏)
- 直接修改
store.state,而不是使用setState - 在选择器中创建新的对象引用,却没有使用
shallow - 不带选择器使用
useStore(会订阅所有内容) - Effect 应立即运行时忘记设置
eager: true
评论
支持 Markdown