闽公网安备 35020302035485号
.所有页面都能访问和修改 tab 状态
// 定义单个 tab 的结构
interface TabItem {
id: string
title: string
}
// 堆代码 duidaima.com
// 整个 store 的结构
interface TabState {
tabs: TabItem[]
currentTabId: string
addTab: (tab: TabItem) =>void
removeTab: (id: string) =>void
setCurrentTab: (id: string) =>void
}
然后用 Zustand 创建一个 store:import { create } from 'zustand'
export const useTabStore = create<TabState>((set) => ({
tabs: [],
currentTabId: '',
addTab: (tab) => set((state) => ({ tabs: [...state.tabs, tab] })),
removeTab: (id) => set((state) => ({
tabs: state.tabs.filter((t) => t.id !== id),
currentTabId: state.currentTabId === id ? '' : state.currentTabId
})),
setCurrentTab: (id) => set({ currentTabId: id })
}))
是不是很简洁?一个 create() 函数就搞定共享状态,类型提示也直接到位。import { useTabStore } from'./store/tabStore'
exportdefaultfunction TabView() {
const { tabs, currentTabId, addTab, setCurrentTab } = useTabStore()
return (
<div>
<button onClick={() => addTab({ id: 'settings', title: '设置' })}>
新增 Tab
</button>
<div style={{ display: 'flex', gap: 8 }}>
{tabs.map((tab) => (
<div
key={tab.id}
onClick={() => setCurrentTab(tab.id)}
style={{
padding: 4,
borderBottom: tab.id === currentTabId ? '2px solid blue' : 'none',
cursor: 'pointer'
}}
>
{tab.title}
</div>
))}
</div>
</div>
)
}
现在这个组件可以随时新增 Tab、切换当前 Tab,所有状态都由 Zustand 管理,其他组件也能随时访问它,非常方便。import { create } from'zustand';
// 创建一个包含多个字段的状态
const useAppStore = create((set) => ({
// 用户信息
user: {
name: '张三',
age: 25,
isLogin: true
},
// 主题设置
theme: 'light',
// 修改主题的方法
setTheme: (newTheme) =>set({ theme: newTheme }),
// 修改用户年龄的方法
setUserAge: (age) =>set((state) => ({ user: { ...state.user, age } }))
}));
如果组件直接订阅整个状态对象,即使只用到其中一个字段,当状态中任何字段变化时,组件都会重新渲染:// 组件 A:只需要显示用户名
function UserName() {
// 订阅了整个状态对象(错误示范)
const { user } = useAppStore();
console.log('UserName 组件渲染了');
return <div>用户名:{user.name}</div>;
}
// 组件 B:只需要显示主题
function ThemeDisplay() {
// 同样订阅了整个状态对象(
const { theme } = useAppStore();
console.log('ThemeDisplay 组件渲染了');
return <div>当前主题:{theme}</div>;
}
当调用 setUserAge(26) 修改用户年龄时,user 对象变化 → 即使 ThemeDisplay 只用到 theme(未变化),也会被强制重新渲染,控制台会打印 ThemeDisplay 组件渲染了。// 组件 A:只订阅 user.name
function UserName() {
// 精确选择需要的字段
const userName = useAppStore((state) => state.user.name);
console.log('UserName 组件渲染了');
return <div>用户名:{userName}</div>;
}
// 组件 B:只订阅 theme
function ThemeDisplay() {
// 精确选择需要的字段
const theme = useAppStore((state) => state.theme);
console.log('ThemeDisplay 组件渲染了');
return <div>当前主题:{theme}</div>;
}
当调用 setUserAge(26) 时,只有 user.age 变化,user.name 未变 → UserName 组件不会重新渲染。当调用 setTheme('dark') 时,theme 变化 → 只有 ThemeDisplay 组件重新渲染,UserName 不受影响。
import { persist } from 'zustand/middleware'
const useUserStore = create(
persist(
(set) => ({
token: '',
setToken: (val) => set({ token: val }),
}),
{
name: 'user-storage', // localStorage key
}
)
)
下次页面加载会自动恢复状态,无需你手动做缓存逻辑。import { devtools } from 'zustand/middleware'
const useStore = create(devtools((set) => ({
count: 0,
increase: () => set((state) => ({ count: state.count + 1 }))
})))
开起来直接就能调状态了,兼容性也很好。3.模块化拆分 store,保持单一职责,一个功能一个 store 文件,利于维护和团队协作。
stores/tabStore.ts:管理多页签状态 stores/userStore.ts:管理用户登录态 stores/formStore.ts:表单缓存数据 stores/themeStore.ts:主题设置每个 Store 内部维护自己的状态结构和操作函数,只暴露对应的 useXXXStore hook。
// stores/userStore.ts
interface UserState {
token: string
login: (token: string) => void
logout: () => void
}
export const useUserStore = create<UserState>((set) => ({
token: '',
login: (token) => set({ token }),
logout: () => set({ token: '' }),
}))
其他组件中直接使用:const { token, login } = useUserStore()
这样做的好处是:3.对新成员更友好,知道状态在哪改、在哪用
4.还能支持持久化、选择订阅、调试工具,非常全面
对我来说,Zustand 就是那个 刚刚好 的选择:干净利落、类型清晰、可扩展性强,非常适合中小型项目的快速开发。
附上zustand官网地址:https://github.com/pmndrs/zustand