Elysia Tools

在一个清晰的工作台里浏览工具、案例、可视化和主题指南。

热门分类

专题

语言
ZHESPTRUFRDEEN

Elysia Tools

导航

状态管理

Zustand 示例

Zustand 状态管理库示例,包括基础存储设置、中间件、持久化和高级模式

案例

这个案例集合中的条目

代码typescript简单

基础 Zustand 存储

基础的 Zustand 存储模式,包括创建、更新和在React组件中使用状态

难度

3/10

预计时间

20 min

标签

zustand, state-management, react, typescript

前置条件

React basics, TypeScript, Hooks

typescript
// Zustand Basic Store Examples
// Zustand is a small, fast, and scalable state management solution for React

// 1. Basic Counter Store
import React, { useState, useEffect, useCallback } from 'react'
import { create } from 'zustand'

interface CounterState {
  count: number
  increment: () => void
  decrement: () => void
  reset: () => void
}

const useCounterStore = create<CounterState>((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
  decrement: () => set((state) => ({ count: state.count - 1 })),
  reset: () => set({ count: 0 }),
}))

// Usage in component
function Counter() {
  const { count, increment, decrement, reset } = useCounterStore()

  return (
    <div className="p-4 border rounded-lg">
      <h2 className="text-xl font-bold mb-4">Counter: {count}</h2>
      <div className="space-x-2">
        <button onClick={increment} className="px-4 py-2 bg-blue-500 text-white rounded">
          Increment
        </button>
        <button onClick={decrement} className="px-4 py-2 bg-red-500 text-white rounded">
          Decrement
        </button>
        <button onClick={reset} className="px-4 py-2 bg-gray-500 text-white rounded">
          Reset
        </button>
      </div>
    </div>
  )
}

// 2. Todo Store with CRUD Operations
interface Todo {
  id: string
  text: string
  completed: boolean
  createdAt: Date
}

interface TodoState {
  todos: Todo[]
  addTodo: (text: string) => void
  toggleTodo: (id: string) => void
  deleteTodo: (id: string) => void
  updateTodo: (id: string, text: string) => void
  clearCompleted: () => void
  completedCount: number
  totalCount: number
}

const useTodoStore = create<TodoState>((set, get) => ({
  todos: [],
  addTodo: (text: string) => {
    const newTodo: Todo = {
      id: Date.now().toString(),
      text,
      completed: false,
      createdAt: new Date(),
    }
    set((state) => ({ todos: [...state.todos, newTodo] }))
  },
  toggleTodo: (id: string) =>
    set((state) => ({
      todos: state.todos.map((todo) =>
        todo.id === id ? { ...todo, completed: !todo.completed } : todo
      ),
    })),
  deleteTodo: (id: string) =>
    set((state) => ({
      todos: state.todos.filter((todo) => todo.id !== id),
    })),
  updateTodo: (id: string, text: string) =>
    set((state) => ({
      todos: state.todos.map((todo) =>
        todo.id === id ? { ...todo, text } : todo
      ),
    })),
  clearCompleted: () =>
    set((state) => ({
      todos: state.todos.filter((todo) => !todo.completed),
    })),
  get completedCount() {
    return get().todos.filter((todo) => todo.completed).length
  },
  get totalCount() {
    return get().todos.length
  },
}))

// Todo Component
function TodoList() {
  const {
    todos,
    addTodo,
    toggleTodo,
    deleteTodo,
    updateTodo,
    clearCompleted,
    completedCount,
    totalCount,
  } = useTodoStore()

  const [newTodoText, setNewTodoText] = useState('')

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault()
    if (newTodoText.trim()) {
      addTodo(newTodoText.trim())
      setNewTodoText('')
    }
  }

  return (
    <div className="max-w-md mx-auto p-4">
      <h1 className="text-2xl font-bold mb-4">Todo List</h1>

      <form onSubmit={handleSubmit} className="mb-4">
        <div className="flex space-x-2">
          <input
            type="text"
            value={newTodoText}
            onChange={(e) => setNewTodoText(e.target.value)}
            placeholder="Add a new todo..."
            className="flex-1 px-3 py-2 border rounded"
          />
          <button type="submit" className="px-4 py-2 bg-blue-500 text-white rounded">
            Add
          </button>
        </div>
      </form>

      <div className="mb-4 text-sm text-gray-600">
        {completedCount} of {totalCount} completed
      </div>

      <ul className="space-y-2">
        {todos.map((todo) => (
          <li key={todo.id} className="flex items-center space-x-2">
            <input
              type="checkbox"
              checked={todo.completed}
              onChange={() => toggleTodo(todo.id)}
              className="w-4 h-4"
            />
            <input
              type="text"
              value={todo.text}
              onChange={(e) => updateTodo(todo.id, e.target.value)}
              className={`flex-1 px-2 py-1 border rounded ${
                todo.completed ? 'line-through text-gray-500' : ''
              }`}
            />
            <button
              onClick={() => deleteTodo(todo.id)}
              className="px-2 py-1 bg-red-500 text-white rounded text-sm"
            >
              Delete
            </button>
          </li>
        ))}
      </ul>

      {completedCount > 0 && (
        <button
          onClick={clearCompleted}
          className="mt-4 px-4 py-2 bg-gray-500 text-white rounded"
        >
          Clear Completed ({completedCount})
        </button>
      )}
    </div>
  )
}

// 3. Shopping Cart Store
interface CartItem {
  id: string
  name: string
  price: number
  quantity: number
  image?: string
}

interface CartState {
  items: CartItem[]
  addItem: (item: Omit<CartItem, 'quantity'>) => void
  removeItem: (id: string) => void
  updateQuantity: (id: string, quantity: number) => void
  clearCart: () => void
  getTotalPrice: () => number
  getTotalItems: () => number
  isInCart: (id: string) => boolean
}

const useCartStore = create<CartState>((set, get) => ({
  items: [],
  addItem: (item) =>
    set((state) => {
      const existingItem = state.items.find((i) => i.id === item.id)
      if (existingItem) {
        return {
          items: state.items.map((i) =>
            i.id === item.id
              ? { ...i, quantity: i.quantity + 1 }
              : i
          ),
        }
      }
      return { items: [...state.items, { ...item, quantity: 1 }] }
    }),
  removeItem: (id) =>
    set((state) => ({
      items: state.items.filter((item) => item.id !== id),
    })),
  updateQuantity: (id, quantity) =>
    set((state) => ({
      items: state.items.map((item) =>
        item.id === id ? { ...item, quantity: Math.max(1, quantity) } : item
      ),
    })),
  clearCart: () => set({ items: [] }),
  getTotalPrice: () =>
    get().items.reduce((total, item) => total + item.price * item.quantity, 0),
  getTotalItems: () =>
    get().items.reduce((total, item) => total + item.quantity, 0),
  isInCart: (id) => get().items.some((item) => item.id === id),
}))

// Cart Component
function ShoppingCart() {
  const {
    items,
    addItem,
    removeItem,
    updateQuantity,
    clearCart,
    getTotalPrice,
    getTotalItems,
  } = useCartStore()

  const sampleProducts = [
    { id: '1', name: 'Laptop', price: 999, image: '/laptop.jpg' },
    { id: '2', name: 'Mouse', price: 29, image: '/mouse.jpg' },
    { id: '3', name: 'Keyboard', price: 79, image: '/keyboard.jpg' },
  ]

  return (
    <div className="max-w-4xl mx-auto p-4">
      <h1 className="text-2xl font-bold mb-6">Shopping Cart</h1>

      <div className="grid md:grid-cols-2 gap-8">
        {/* Products */}
        <div>
          <h2 className="text-xl font-semibold mb-4">Products</h2>
          <div className="space-y-4">
            {sampleProducts.map((product) => (
              <div key={product.id} className="border p-4 rounded-lg">
                <div className="flex justify-between items-start">
                  <div>
                    <h3 className="font-semibold">{product.name}</h3>
                    <p className="text-gray-600">${product.price}</p>
                  </div>
                  <button
                    onClick={() => addItem(product)}
                    className="px-3 py-1 bg-blue-500 text-white rounded"
                  >
                    Add to Cart
                  </button>
                </div>
              </div>
            ))}
          </div>
        </div>

        {/* Cart */}
        <div>
          <h2 className="text-xl font-semibold mb-4">
            Cart ({getTotalItems()} items)
          </h2>

          {items.length === 0 ? (
            <p className="text-gray-500">Your cart is empty</p>
          ) : (
            <>
              <div className="space-y-2 mb-4">
                {items.map((item) => (
                  <div key={item.id} className="border p-3 rounded">
                    <div className="flex justify-between items-center">
                      <div>
                        <h4 className="font-semibold">{item.name}</h4>
                        <p className="text-gray-600">${item.price}</p>
                      </div>
                      <div className="flex items-center space-x-2">
                        <input
                          type="number"
                          min="1"
                          value={item.quantity}
                          onChange={(e) => {
                            const nextQuantity = Number(e.target.value)
                            updateQuantity(item.id, Number.isFinite(nextQuantity) ? nextQuantity : 1)
                          }}
                          className="w-16 px-2 py-1 border rounded"
                        />
                        <button
                          onClick={() => removeItem(item.id)}
                          className="px-2 py-1 bg-red-500 text-white rounded text-sm"
                        >
                          Remove
                        </button>
                      </div>
                    </div>
                    <p className="text-sm text-gray-600 mt-1">
                      Subtotal: ${(item.price * item.quantity).toFixed(2)}
                    </p>
                  </div>
                ))}
              </div>

              <div className="border-t pt-4">
                <div className="flex justify-between text-lg font-semibold">
                  <span>Total:</span>
                  <span>${getTotalPrice().toFixed(2)}</span>
                </div>
                <div className="mt-4 space-x-2">
                  <button className="px-4 py-2 bg-green-500 text-white rounded">
                    Checkout
                  </button>
                  <button
                    onClick={clearCart}
                    className="px-4 py-2 bg-gray-500 text-white rounded"
                  >
                    Clear Cart
                  </button>
                </div>
              </div>
            </>
          )}
        </div>
      </div>
    </div>
  )
}

// 4. User Authentication Store
interface User {
  id: string
  name: string
  email: string
  avatar?: string
  role: 'admin' | 'user'
}

interface AuthState {
  user: User | null
  isAuthenticated: boolean
  isLoading: boolean
  login: (email: string, password: string) => Promise<void>
  logout: () => void
  register: (name: string, email: string, password: string) => Promise<void>
  updateProfile: (data: Partial<User>) => Promise<void>
}

const useAuthStore = create<AuthState>((set, get) => ({
  user: null,
  isAuthenticated: false,
  isLoading: false,

  login: async (email: string, password: string) => {
    set({ isLoading: true })
    try {
      // Simulate API call
      await new Promise((resolve) => setTimeout(resolve, 1000))

      // Mock successful login
      const user: User = {
        id: '1',
        name: 'John Doe',
        email,
        role: 'user',
        avatar: 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e',
      }

      set({ user, isAuthenticated: true, isLoading: false })
    } catch (error) {
      set({ isLoading: false })
      throw error
    }
  },

  logout: () => {
    set({ user: null, isAuthenticated: false })
  },

  register: async (name: string, email: string, password: string) => {
    set({ isLoading: true })
    try {
      // Simulate API call
      await new Promise((resolve) => setTimeout(resolve, 1000))

      const user: User = {
        id: Date.now().toString(),
        name,
        email,
        role: 'user',
      }

      set({ user, isAuthenticated: true, isLoading: false })
    } catch (error) {
      set({ isLoading: false })
      throw error
    }
  },

  updateProfile: async (data: Partial<User>) => {
    const currentUser = get().user
    if (!currentUser) return

    try {
      // Simulate API call
      await new Promise((resolve) => setTimeout(resolve, 500))

      const updatedUser = { ...currentUser, ...data }
      set({ user: updatedUser })
    } catch (error) {
      throw error
    }
  },
}))

// Authentication Components
function LoginForm() {
  const { login, isLoading } = useAuthStore()
  const [email, setEmail] = useState('')
  const [password, setPassword] = useState('')

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault()
    try {
      await login(email, password)
    } catch (error) {
      alert('Login failed')
    }
  }

  return (
    <form onSubmit={handleSubmit} className="max-w-md mx-auto p-4">
      <h2 className="text-xl font-bold mb-4">Login</h2>
      <div className="space-y-4">
        <input
          type="email"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
          placeholder="Email"
          className="w-full px-3 py-2 border rounded"
          required
        />
        <input
          type="password"
          value={password}
          onChange={(e) => setPassword(e.target.value)}
          placeholder="Password"
          className="w-full px-3 py-2 border rounded"
          required
        />
        <button
          type="submit"
          disabled={isLoading}
          className="w-full px-4 py-2 bg-blue-500 text-white rounded disabled:opacity-50"
        >
          {isLoading ? 'Logging in...' : 'Login'}
        </button>
      </div>
    </form>
  )
}

function UserProfile() {
  const { user, logout, updateProfile } = useAuthStore()
  const [isEditing, setIsEditing] = useState(false)
  const [editName, setEditName] = useState(user?.name || '')

  const handleUpdate = async () => {
    try {
      await updateProfile({ name: editName })
      setIsEditing(false)
    } catch (error) {
      alert('Update failed')
    }
  }

  return (
    <div className="max-w-md mx-auto p-4">
      <h2 className="text-xl font-bold mb-4">Profile</h2>

      <div className="space-y-4">
        {user?.avatar && (
          <img
            src={user.avatar}
            alt={user.name}
            className="w-20 h-20 rounded-full"
          />
        )}

        <div>
          {isEditing ? (
            <div className="flex space-x-2">
              <input
                type="text"
                value={editName}
                onChange={(e) => setEditName(e.target.value)}
                className="flex-1 px-3 py-2 border rounded"
              />
              <button
                onClick={handleUpdate}
                className="px-3 py-2 bg-green-500 text-white rounded"
              >
                Save
              </button>
              <button
                onClick={() => setIsEditing(false)}
                className="px-3 py-2 bg-gray-500 text-white rounded"
              >
                Cancel
              </button>
            </div>
          ) : (
            <div>
              <p className="font-semibold">{user?.name}</p>
              <p className="text-gray-600">{user?.email}</p>
              <p className="text-sm text-gray-500">Role: {user?.role}</p>
              <button
                onClick={() => {
                  setEditName(user?.name || '')
                  setIsEditing(true)
                }}
                className="mt-2 px-3 py-1 bg-blue-500 text-white rounded text-sm"
              >
                Edit Profile
              </button>
            </div>
          )}
        </div>

        <button
          onClick={logout}
          className="px-4 py-2 bg-red-500 text-white rounded"
        >
          Logout
        </button>
      </div>
    </div>
  )
}
代码typescript中等

高级 Zustand 模式

复杂的 Zustand 模式,包括中间件、持久化、选择器和性能优化

难度

7/10

预计时间

40 min

标签

zustand, advanced, state-management, middleware

前置条件

Zustand basics, React hooks, TypeScript, Testing fundamentals

typescript
// Advanced Zustand Patterns

// 1. Persistence with Middleware
import { create } from 'zustand'
import { persist, createJSONStorage } from 'zustand/middleware'

interface UserPreferences {
  theme: 'light' | 'dark'
  language: string
  notifications: boolean
  fontSize: 'small' | 'medium' | 'large'
}

const usePreferencesStore = create<UserPreferences>()(
  persist(
    (set) => ({
      theme: 'light',
      language: 'en',
      notifications: true,
      fontSize: 'medium',
      setTheme: (theme) => set({ theme }),
      setLanguage: (language) => set({ language }),
      toggleNotifications: () => set((state) => ({
        notifications: !state.notifications
      })),
      setFontSize: (fontSize) => set({ fontSize }),
    }),
    {
      name: 'user-preferences',
      storage: createJSONStorage(() => localStorage),
      partialize: (state) => ({
        theme: state.theme,
        language: state.language,
        fontSize: state.fontSize,
      }),
    }
  )
)

// 2. DevTools Middleware
import { devtools } from 'zustand/middleware'

interface AppStore {
  users: User[]
  posts: Post[]
  addUser: (user: User) => void
  addPost: (post: Post) => void
  removeUser: (id: string) => void
  removePost: (id: string) => void
}

const useAppStore = create<AppStore>()(
  devtools(
    (set) => ({
      users: [],
      posts: [],
      addUser: (user) => set((state) => ({
        users: [...state.users, user]
      })),
      addPost: (post) => set((state) => ({
        posts: [...state.posts, post]
      })),
      removeUser: (id) => set((state) => ({
        users: state.users.filter(user => user.id !== id)
      })),
      removePost: (id) => set((state) => ({
        posts: state.posts.filter(post => post.id !== id)
      })),
    }),
    {
      name: 'app-store',
      anonymousActionType: 'unknown',
    }
  )
)

// 3. Subscribe to Store Changes
const useLogger = () => {
  const subscribe = useAppStore.subscribe

  useEffect(() => {
    const unsubscribe = subscribe((state) => {
      console.log('Store updated:', state)
    })

    return unsubscribe
  }, [subscribe])
}

// Select specific state changes
useAppStore.subscribe(
  (state) => state.users,
  (users) => {
    console.log('Users changed:', users)
  }
)

// 4. Combining Multiple Stores
const useCombinedStore = () => {
  const preferences = usePreferencesStore()
  const cart = useCartStore()

  const getLocalizedPrice = (price: number) => {
    const formatter = new Intl.NumberFormat(preferences.language, {
      style: 'currency',
      currency: preferences.language === 'en' ? 'USD' : 'EUR',
    })
    return formatter.format(price)
  }

  return {
    ...preferences,
    ...cart,
    getLocalizedPrice,
  }
}

// 5. Async Actions with Error Handling
interface AsyncStore {
  data: any[]
  loading: boolean
  error: string | null
  fetchData: () => Promise<void>
  createItem: (item: any) => Promise<void>
  updateItem: (id: string, updates: any) => Promise<void>
  deleteItem: (id: string) => Promise<void>
  clearError: () => void
}

const useAsyncStore = create<AsyncStore>((set, get) => ({
  data: [],
  loading: false,
  error: null,

  clearError: () => set({ error: null }),

  fetchData: async () => {
    set({ loading: true, error: null })
    try {
      const response = await fetch('/api/data')
      const data = await response.json()
      set({ data, loading: false })
    } catch (error) {
      set({
        error: error instanceof Error ? error.message : 'Failed to fetch data',
        loading: false
      })
    }
  },

  createItem: async (item) => {
    set({ loading: true, error: null })
    try {
      const response = await fetch('/api/data', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(item),
      })
      const newItem = await response.json()

      set((state) => ({
        data: [...state.data, newItem],
        loading: false
      }))
    } catch (error) {
      set({
        error: error instanceof Error ? error.message : 'Failed to create item',
        loading: false
      })
    }
  },

  updateItem: async (id, updates) => {
    set({ loading: true, error: null })
    try {
      const response = await fetch(`/api/data/${id}`, {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(updates),
      })
      const updatedItem = await response.json()

      set((state) => ({
        data: state.data.map(item =>
          item.id === id ? { ...item, ...updatedItem } : item
        ),
        loading: false
      }))
    } catch (error) {
      set({
        error: error instanceof Error ? error.message : 'Failed to update item',
        loading: false
      })
    }
  },

  deleteItem: async (id) => {
    set({ loading: true, error: null })
    try {
      await fetch(`/api/data/${id}`, { method: 'DELETE' })

      set((state) => ({
        data: state.data.filter(item => item.id !== id),
        loading: false
      }))
    } catch (error) {
      set({
        error: error instanceof Error ? error.message : 'Failed to delete item',
        loading: false
      })
    }
  },
}))

// 6. Custom Selectors for Performance
const useGameStore = create<GameStore>()(
  devtools(
    persist(
      (set, get) => ({
        players: [],
        enemies: [],
        items: [],
        score: 0,
        level: 1,
        health: 100,

        // Actions
        addPlayer: (player) => set((state) => ({
          players: [...state.players, player]
        })),

        spawnEnemy: (enemy) => set((state) => ({
          enemies: [...state.enemies, enemy]
        })),

        collectItem: (item) => set((state) => ({
          items: [...state.items, item],
          score: state.score + item.points
        })),

        takeDamage: (damage) => set((state) => ({
          health: Math.max(0, state.health - damage)
        })),

        levelUp: () => set((state) => ({
          level: state.level + 1,
          health: state.health + 20
        })),
      }),
      {
        name: 'game-store',
        storage: createJSONStorage(() => localStorage),
      }
    )
  )
)

// Custom selectors with memoization
const useGameSelectors = () => {
  const store = useGameStore()

  const aliveEnemies = useStore(useCallback(
    (state) => state.enemies.filter(enemy => enemy.health > 0),
    []
  ))

  const playerHealth = useStore(useCallback(
    (state) => state.health,
    []
  ))

  const isGameOver = useStore(useCallback(
    (state) => state.health <= 0,
    []
  ))

  const highScore = useStore(useCallback(
    (state) => state.score,
    []
  ))

  return {
    aliveEnemies,
    playerHealth,
    isGameOver,
    highScore,
    ...store
  }
}

// 7. Optimistic Updates
interface OptimisticStore {
  posts: Post[]
  updatePostOptimistic: (id: string, updates: Partial<Post>) => Promise<void>
  deletePostOptimistic: (id: string) => Promise<void>
}

const useOptimisticStore = create<OptimisticStore>((set, get) => ({
  posts: [],

  updatePostOptimistic: async (id, updates) => {
    const previousPosts = get().posts

    // Optimistic update
    set((state) => ({
      posts: state.posts.map(post =>
        post.id === id ? { ...post, ...updates } : post
      )
    }))

    try {
      const response = await fetch(`/api/posts/${id}`, {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(updates),
      })

      if (!response.ok) {
        throw new Error('Update failed')
      }

      const updatedPost = await response.json()

      // Confirm update
      set((state) => ({
        posts: state.posts.map(post =>
          post.id === id ? updatedPost : post
        )
      }))
    } catch (error) {
      // Revert on error
      set({ posts: previousPosts })
      throw error
    }
  },

  deletePostOptimistic: async (id) => {
    const previousPosts = get().posts

    // Optimistic deletion
    set((state) => ({
      posts: state.posts.filter(post => post.id !== id)
    }))

    try {
      const response = await fetch(`/api/posts/${id}`, {
        method: 'DELETE',
      })

      if (!response.ok) {
        throw new Error('Delete failed')
      }
    } catch (error) {
      // Revert on error
      set({ posts: previousPosts })
      throw error
    }
  },
}))

// 8. Testing Zustand Stores
import { act, renderHook } from '@testing-library/react'
import { create } from 'zustand'

// Test store
interface TestStore {
  count: number
  increment: () => void
  decrement: () => void
}

const useTestStore = create<TestStore>((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
  decrement: () => set((state) => ({ count: state.count - 1 })),
}))

// Test cases
describe('useTestStore', () => {
  it('should initialize with count 0', () => {
    const { result } = renderHook(() => useTestStore())
    expect(result.current.count).toBe(0)
  })

  it('should increment count', () => {
    const { result } = renderHook(() => useTestStore())

    act(() => {
      result.current.increment()
    })

    expect(result.current.count).toBe(1)
  })

  it('should decrement count', () => {
    const { result } = renderHook(() => useTestStore())

    act(() => {
      result.current.decrement()
    })

    expect(result.current.count).toBe(-1)
  })
})

// 9. TypeScript Best Practices
interface StrictStore {
  readonly state: {
    user: User | null
    status: 'idle' | 'loading' | 'success' | 'error'
  }
  actions: {
    setUser: (user: User) => void
    clearUser: () => void
    setStatus: (status: StrictStore['state']['status']) => void
  }
}

const useStrictStore = create<StrictStore>((set) => ({
  state: {
    user: null,
    status: 'idle' as const,
  },
  actions: {
    setUser: (user) =>
      set((state) => ({
        state: { ...state.state, user }
      })),
    clearUser: () =>
      set((state) => ({
        state: { ...state.state, user: null }
      })),
    setStatus: (status) =>
      set((state) => ({
        state: { ...state.state, status }
      })),
  },
}))

// Usage with destructuring
const { state: { user, status }, actions } = useStrictStore()

// 10. Server-Side Rendering Support
import { create } from 'zustand'
import { createContext, useContext, useRef } from 'react'

// Store interface
interface Store {
  count: number
  increment: () => void
}

// Create store
const createStore = () => create<Store>((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
}))

// Context for store
const StoreContext = createContext<ReturnType<typeof createStore> | null>(null)

// Provider component
export const StoreProvider = ({ children }: { children: React.ReactNode }) => {
  const storeRef = useRef<ReturnType<typeof createStore>>()

  if (!storeRef.current) {
    storeRef.current = createStore()
  }

  return (
    <StoreContext.Provider value={storeRef.current}>
      {children}
    </StoreContext.Provider>
  )
}

// Hook to use store
export const useStore = <T>(selector: (store: Store) => T): T => {
  const store = useContext(StoreContext)

  if (!store) {
    throw new Error('useStore must be used within StoreProvider')
  }

  return useStore(store, selector)
}

工具

经常与该案例搭配使用的工具

相关内容

相关案例集合

工具使用指南

帮助搜索与理解工作流的案例说明

标题

Zustand 示例

描述

Zustand 状态管理库示例,包括基础存储设置、中间件、持久化和高级模式

概览

Zustand 状态管理库示例,包括基础存储设置、中间件、持久化和高级模式

适用场景

  • Use this sample set when you want reference inputs for Zustand 示例.
  • Use it to preview output formats and compare related sample variants.

使用方式

  1. 1Open a sample entry to inspect the prepared example payload or asset.
  2. 2Compare it with related tools to understand how the workflow connects.

常见用途

  • 基础 Zustand 存储
  • 高级 Zustand 模式

常见问题

What is included in Zustand 示例?

This page includes curated sample entries, related tools, and neighboring sample collections for Zustand 示例.

首页
工具3197
案例593
可视化461
文件查看器103
专题229
标签333
图像、音频与视频655

图像、音频、视频处理、转换与优化工具

数学、日期与金融644

计算器、数值计算、日期逻辑、统计与金融工具

设计与颜色296

颜色、版式、图形、视觉样式与设计辅助工具

文本与写作209

写作、清洗、格式化、提取与文本分析工具

开发与 Web193

开发者工具、网络处理、Web 调试与自动化辅助工具

转换与编码175

格式、文件、压缩包、单位与编码转换工具

界面截图结构化、人工校对与开发交付
钢琴旋律草稿创作、MIDI 核验与试听样稿交付
复古游戏纹理制作、候选评审与素材交付
操作截图到可复核图文指南与培训交付
网页改版响应式视觉回归审核与证据交付

© 2026 Elysia Tools. 保留所有权利。

文档价格关于我们服务条款隐私政策Cookie 政策联系我们