使用 Store #
基本使用 #
订阅整个 Store #
最简单的使用方式是订阅整个 store:
tsx
import { create } from 'zustand'
const useStore = create((set) => ({
count: 0,
name: 'Zustand',
increment: () => set((state) => ({ count: state.count + 1 })),
}))
function Component() {
// 订阅整个 store,任何状态变化都会触发重渲染
const store = useStore()
return (
<div>
<p>Count: {store.count}</p>
<p>Name: {store.name}</p>
<button onClick={store.increment}>Increment</button>
</div>
)
}
订阅特定状态 #
使用选择器只订阅需要的状态:
tsx
function Counter() {
// 只订阅 count,name 变化不会触发重渲染
const count = useStore((state) => state.count)
const increment = useStore((state) => state.increment)
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
)
}
function NameDisplay() {
// 只订阅 name
const name = useStore((state) => state.name)
return <p>Name: {name}</p>
}
解构使用 #
tsx
function Component() {
// 解构获取多个值
const { count, name, increment } = useStore((state) => ({
count: state.count,
name: state.name,
increment: state.increment,
}))
return (
<div>
<p>Count: {count}</p>
<p>Name: {name}</p>
<button onClick={increment}>Increment</button>
</div>
)
}
选择器(Selector) #
什么是选择器? #
选择器是一个函数,用于从 store 中提取特定的状态:
tsx
// 选择器函数
(state) => state.count
// 在组件中使用
const count = useStore((state) => state.count)
选择器的优势 #
text
选择器优势
├── 性能优化 ──── 只订阅需要的状态
├── 避免重渲染 ── 减少不必要的渲染
├── 数据转换 ──── 可以在订阅时转换数据
└── 计算属性 ──── 派生状态计算
基本选择器 #
tsx
// 选择单个值
const count = useStore((state) => state.count)
// 选择多个值
const { count, name } = useStore((state) => ({
count: state.count,
name: state.name,
}))
// 计算派生值
const doubleCount = useStore((state) => state.count * 2)
// 条件选择
const isAdmin = useStore((state) => state.user?.role === 'admin')
复杂选择器 #
tsx
interface Todo {
id: string
text: string
completed: boolean
}
interface TodoState {
todos: Todo[]
filter: 'all' | 'active' | 'completed'
}
const useTodoStore = create<TodoState>((set) => ({
todos: [],
filter: 'all',
}))
function TodoList() {
// 根据过滤条件选择 todos
const filteredTodos = useTodoStore((state) => {
switch (state.filter) {
case 'active':
return state.todos.filter((todo) => !todo.completed)
case 'completed':
return state.todos.filter((todo) => todo.completed)
default:
return state.todos
}
})
return (
<ul>
{filteredTodos.map((todo) => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
)
}
选择器性能优化 #
浅比较 #
当选择器返回对象时,需要使用 shallow 比较:
tsx
import { shallow } from 'zustand/shallow'
function Component() {
// 不使用 shallow:每次渲染都会创建新对象,导致重渲染
const { count, name } = useStore((state) => ({
count: state.count,
name: state.name,
}))
// 使用 shallow:浅比较对象属性
const { count, name } = useStore(
(state) => ({ count: state.count, name: state.name }),
shallow
)
return <div>{count} - {name}</div>
}
自定义比较函数 #
tsx
function Component() {
// 自定义比较函数
const todos = useTodoStore(
(state) => state.todos,
(prev, next) => {
// 只有当 todos 数量变化时才重渲染
return prev.length === next.length
}
)
return <div>Total: {todos.length}</div>
}
在组件外使用 #
Zustand 可以在 React 组件外部使用,这是它的一大优势。
获取状态 #
tsx
// 获取当前状态
const state = useStore.getState()
console.log(state.count)
// 获取特定值
const count = useStore.getState().count
更新状态 #
tsx
// 直接更新状态
useStore.setState({ count: 10 })
// 基于前一个状态更新
useStore.setState((state) => ({ count: state.count + 1 }))
订阅状态变化 #
tsx
// 订阅所有变化
const unsubscribe = useStore.subscribe((state, prevState) => {
console.log('State changed:', prevState, '->', state)
})
// 取消订阅
unsubscribe()
实际应用场景 #
在 API 请求中使用 #
tsx
// api/user.ts
import { useUserStore } from '../stores/userStore'
export async function fetchUser(id: string) {
useUserStore.getState().setLoading(true)
try {
const response = await fetch(`/api/users/${id}`)
const user = await response.json()
useUserStore.setState({ user, isLoading: false })
} catch (error) {
useUserStore.setState({ error: error.message, isLoading: false })
}
}
在事件监听器中使用 #
tsx
// utils/websocket.ts
import { useMessageStore } from '../stores/messageStore'
const ws = new WebSocket('ws://localhost:8080')
ws.onmessage = (event) => {
const message = JSON.parse(event.data)
useMessageStore.getState().addMessage(message)
}
在定时器中使用 #
tsx
// utils/autoSave.ts
import { useDocumentStore } from '../stores/documentStore'
setInterval(() => {
const { document, saveDocument } = useDocumentStore.getState()
if (document.isDirty) {
saveDocument()
}
}, 30000)
高级用法 #
条件订阅 #
tsx
function Component({ userId }: { userId: string | null }) {
// 条件订阅
const user = useStore((state) =>
userId ? state.users[userId] : null
)
return user ? <UserProfile user={user} /> : null
}
动态 Store 选择 #
tsx
const stores = {
user: useUserStore,
cart: useCartStore,
theme: useThemeStore,
}
function Component({ storeName }: { storeName: keyof typeof stores }) {
const store = stores[storeName]
const state = store((state) => state)
return <div>{JSON.stringify(state)}</div>
}
批量更新 #
tsx
function Component() {
const { count, name, increment, setName } = useStore((state) => ({
count: state.count,
name: state.name,
increment: state.increment,
setName: state.setName,
}))
const handleBatchUpdate = () => {
// 批量更新只会触发一次重渲染
useStore.setState((state) => ({
count: state.count + 1,
name: 'New Name',
}))
}
return (
<div>
<p>Count: {count}</p>
<p>Name: {name}</p>
<button onClick={handleBatchUpdate}>Batch Update</button>
</div>
)
}
使用 useShallow #
Zustand 4.x 提供了 useShallow hook:
tsx
import { useShallow } from 'zustand/react/shallow'
function Component() {
// 自动浅比较
const { count, name } = useStore(
useShallow((state) => ({
count: state.count,
name: state.name,
}))
)
return <div>{count} - {name}</div>
}
实际案例 #
用户信息组件 #
tsx
interface User {
id: string
name: string
email: string
avatar: string
}
interface UserState {
user: User | null
isLoading: boolean
error: string | null
login: (email: string, password: string) => Promise<void>
logout: () => void
}
const useUserStore = create<UserState>((set) => ({
user: null,
isLoading: false,
error: null,
login: async (email, password) => { /* ... */ },
logout: () => set({ user: null, error: null }),
}))
function UserAvatar() {
const avatar = useUserStore((state) => state.user?.avatar)
const name = useUserStore((state) => state.user?.name)
if (!avatar) return <div>未登录</div>
return <img src={avatar} alt={name} />
}
function UserName() {
const name = useUserStore((state) => state.user?.name || '游客')
return <span>{name}</span>
}
function LoginButton() {
const user = useUserStore((state) => state.user)
const logout = useUserStore((state) => state.logout)
if (user) {
return <button onClick={logout}>退出登录</button>
}
return <button>登录</button>
}
主题切换 #
tsx
type Theme = 'light' | 'dark'
interface ThemeState {
theme: Theme
toggleTheme: () => void
}
const useThemeStore = create<ThemeState>((set) => ({
theme: 'light',
toggleTheme: () => set((state) => ({
theme: state.theme === 'light' ? 'dark' : 'light'
})),
}))
function ThemeToggle() {
const theme = useThemeStore((state) => state.theme)
const toggleTheme = useThemeStore((state) => state.toggleTheme)
return (
<button onClick={toggleTheme}>
当前主题: {theme}
</button>
)
}
function ThemedComponent() {
const theme = useThemeStore((state) => state.theme)
return (
<div className={`theme-${theme}`}>
{/* 内容 */}
</div>
)
}
性能优化技巧 #
1. 精确订阅 #
tsx
// ❌ 不好:订阅整个 store
const store = useStore()
// ✅ 好:只订阅需要的值
const count = useStore((state) => state.count)
2. 使用 shallow 比较 #
tsx
// ❌ 不好:每次都创建新对象
const { a, b } = useStore((state) => ({ a: state.a, b: state.b }))
// ✅ 好:使用 shallow
const { a, b } = useStore(
(state) => ({ a: state.a, b: state.b }),
shallow
)
3. 避免内联函数 #
tsx
// ❌ 不好:内联函数每次都创建新的
const filtered = useStore((state) =>
state.items.filter(item => item.active)
)
// ✅ 好:使用 useMemo 或将过滤逻辑移到 store
const activeItems = useStore((state) => state.activeItems)
总结 #
使用 Store 的关键点:
- 使用选择器精确订阅需要的状态
- 使用
shallow比较优化对象订阅 - 可以在组件外部使用
getState()和setState() - 使用
subscribe()监听状态变化 - 注意性能优化,避免不必要的重渲染
接下来,让我们学习 Actions 与状态更新,深入掌握状态管理技巧。
最后更新:2026-03-28