react中的zustand 模块化

这是项目结构

user 模块

复制代码
import { create } from "zustand";
interface User {
  id: string;
  name: string;
  email: string;
}
interface UserState {
  user: User | null;
  loading: boolean;
  error: string | null;
  fetchUser: (id: string) => Promise<void>;
  logout: () => void;
}

export const useUserstore = create<UserState>((set) => ({
  user: null,
  loading: false,
  error: null,
  fetchUser: async (id) => {
    set({ loading: true, error: null });
    try {
    } catch (err: any) {
      set({ error: err.message, loading: false });
    }
  },
  logout: () => {
    set({ user: null, loading: false, error: null });
  },
}));

购物车模块

复制代码
// store/useCartStore.ts
import { create } from 'zustand';
import { persist } from 'zustand/middleware';

interface CartItem {
  id: string;
  name: string;
  price: number;
  quantity: number;
}

interface CartState {
  items: CartItem[];
  total: number;
  addItem: (item: CartItem) => void;
  removeItem: (id: string) => void;
}

export const useCartStore = create<CartState>()(
  persist(
    (set) => ({
      items: [],
      total: 0,
      addItem: (newItem) =>
        set((state) => {
          const existing = state.items.find((i) => i.id === newItem.id);
          let updatedItems;
          if (existing) {
            updatedItems = state.items.map((i) =>
              i.id === newItem.id ? { ...i, quantity: i.quantity + newItem.quantity } : i
            );
          } else {
            updatedItems = [...state.items, newItem];
          }
          const total = updatedItems.reduce((sum, i) => sum + i.price * i.quantity, 0);
          return { items: updatedItems, total };
        }),
      removeItem: (id) =>
        set((state) => {
          const items = state.items.filter((i) => i.id !== id);
          const total = items.reduce((sum, i) => sum + i.price * i.quantity, 0);
          return { items, total };
        }),
    }),
    { name: 'cart-storage' }
  )
);

主题模块

复制代码
import { create } from "zustand";
type Theme = "light" | "dark";
interface ThemeState {
  theme: Theme;
  setTheme: (theme:Theme) => void;
  toggleTheme: () => void;
}

export const useThemeStore = create<ThemeState>((set) => ({
  theme: 'light',
  setTheme: (theme) => set({ theme }),
  toggleTheme: () => set((state) => ({ theme: state.theme === 'light' ? 'dark' : 'light' })),
}));

在组件内使用多个store

复制代码
// pages/Profile.tsx
import { useThemeStore } from '../store/useThemeStore';
import { useUserStore } from '../store/useUserStore';
import { useCartStore } from '../store/useCartStore';

function Profile() {
  const { theme, toggleTheme } = useThemeStore();
  const { user, logout } = useUserStore();
  const { items, total } = useCartStore();

  return (
    <div className={theme}>
      <h1>{user?.name}</h1>
      <button onClick={toggleTheme}>切换主题</button>
      <button onClick={logout}>退出</button>
      <div>购物车商品数: {items.length},总价: {total}</div>
    </div>
  );
}

每个 store 独立订阅,互不干扰。你可以在一个组件中只订阅某个 store 的一部分状态。

有时一个 store 需要读取或更新另一个 store 的数据。Zustand 允许在 action 中使用 get 参数获取当前 store 的状态,也可以在 store 外部导入其他 store

在 action 中读取其他 store(推荐)

复制代码
// store/useOrderStore.ts
import { create } from 'zustand';
import { useCartStore } from './useCartStore';
import { useUserStore } from './useUserStore';

interface OrderState {
  createOrder: () => Promise<void>;
  loading: boolean;
}

export const useOrderStore = create<OrderState>((set, get) => ({
  loading: false,
  createOrder: async () => {
    // 读取其他 store 的当前值(直接调用 getState,不订阅)
    const { items, total } = useCartStore.getState();
    const { user } = useUserStore.getState();

    if (!user || items.length === 0) return;

    set({ loading: true });
    try {
      await fetch('/api/orders', {
        method: 'POST',
        body: JSON.stringify({ userId: user.id, items, total }),
      });
      // 下单后清空购物车
      useCartStore.getState().clearCart(); // 前提是 useCartStore 中有 clearCart 方法
      set({ loading: false });
    } catch (err) {
      set({ loading: false });
    }
  },
}));

注意:使用 useCartStore.getState()同步获取当前值,不会导致组件重新渲染。如果需要响应式订阅,请在组件中分别调用 hooks。

在 store 内部订阅另一个 store 的变化

复制代码
// store/useLoggerStore.ts
import { create } from 'zustand';
import { useThemeStore } from './useThemeStore';

export const useLoggerStore = create(() => ({
  logThemeChange: () => {
    // 订阅主题变化
    const unsubscribe = useThemeStore.subscribe(
      (state) => state.theme,
      (theme) => {
        console.log('主题已切换为:', theme);
      }
    );
    return unsubscribe;
  },
}));

组合多个 store 成一个大的 store (不推荐,但可选)

如果不想在组件中导入很多个 hooks,也可以创建一个"根 store"整合所有子 store。

复制代码
// store/useRootStore.ts
import { create } from 'zustand';
import { useThemeStore } from './useThemeStore';
import { useUserStore } from './useUserStore';
import { useCartStore } from './useCartStore';

export const useRootStore = create(() => ({
  theme: useThemeStore.getState(),
  user: useUserStore.getState(),
  cart: useCartStore.getState(),
}));

// 手动同步子 store 变化到根 store(复杂且容易遗漏,不推荐)
// 更好的做法:直接用多个 hooks,不要强行合并。
相关推荐
小码哥_常3 小时前
安卓开发秘籍:解锁10大性能优化秘诀
前端
谁呛我名字4 小时前
JavaScript 类型转换与运算规则
javascript
try2find4 小时前
打印ascii码报错问题
java·linux·前端
郑州光合科技余经理4 小时前
同城O2O海外版二次开发实战:从支付网关到配送算法
开发语言·前端·后端·算法·架构·uni-app·php
冰暮流星5 小时前
javascript事件案例-全选框案例
服务器·前端·javascript
Dillon Dong6 小时前
【系列主题】Next.js 16 + Turbopack 的暗礁:深入剖析 Tailwind v4 的 CSS 模块解析陷阱
javascript·css·容器·turbopack
Csvn6 小时前
前端性能优化实战指南
前端
Moment6 小时前
2026 年,AI 全栈时代到了,前端简历别再只写前端技术了 🫠🫠🫠
前端·后端·面试
糯米团子7496 小时前
Web Worker
开发语言·前端·javascript
freewlt6 小时前
React Server Components 深度解析
前端·react.js·前端框架