使用 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