Zustand 是一个轻量、灵活的状态管理库。自从 Zustand v4 推出 Vanilla Store 后,我们可以更优雅地在组件外(如 API 拦截器、工具函数)访问状态,同时在组件内继续享受响应式的状态订阅。
本教程将通过一个“登录状态管理”示例,讲解:
- Vanilla Store 和 useStore 的区别
- 如何在组件外获取状态
- 如何在组件内响应状态变化
- 实战完整代码(TypeScript + Zustand + Axios)
🧠 一图理解 Zustand 状态访问方式
场景 | 用法 | 是否响应式 | 适合位置 |
---|---|---|---|
组件内 | useStore(...) 或封装的 hook(如 useAuthToken() ) | ✅ 是 | React 组件 |
组件外 | store.getState() / store.setState() | ❌ 否 | axios、router、utils 等 |
🔧 Step 1:定义 Zustand Vanilla Store
// stores/authStore.ts
import { createStore, useStore } from "zustand";
import { persist, createJSONStorage } from "zustand/middleware";
import { immer } from "zustand/middleware/immer";
interface AuthState {
token: string | null;
refreshToken: string | null;
userId: string | null;
userName: string | null;
setToken: (token: string, refreshToken: string) => void;
clearToken: () => void;
userLogin: (data: {
token: string;
refreshToken: string;
userId: string;
userName: string;
}) => void;
}
export const authStore = createStore<AuthState>()(
immer(
persist(
(set) => ({
token: null,
refreshToken: null,
userId: null,
userName: null,
setToken: (token, refreshToken) => set({ token, refreshToken }),
clearToken: () =>
set({
token: null,
refreshToken: null,
userId: null,
userName: null,
}),
userLogin: (userData) => {
const { token, refreshToken, userId, userName } = userData;
set({ token, refreshToken, userId, userName });
},
}),
{
name: "auth-storage",
storage: createJSONStorage(() => localStorage),
partialize: (state) => ({
token: state.token,
refreshToken: state.refreshToken,
userId: state.userId,
userName: state.userName,
}),
}
)
)
);
// 自定义 Hook
export const useAuthStore = <T>(selector: (state: AuthState) => T): T =>
useStore(authStore, selector);
export const useAuthToken = () =>
useAuthStore((state) => state.token);
export const useAuthUser = () =>
useAuthStore((state) => ({
userId: state.userId,
userName: state.userName,
}));
export const useAuthActions = () =>
useAuthStore((state) => ({
login: state.userLogin,
logout: state.clearToken,
setToken: state.setToken,
}));
// 外部访问方法
export const getAuthState = () => authStore.getState();
export const setAuthState = authStore.setState;
🚀 Step 2:axios 中使用 token(非组件内)
// utils/axios.ts
import axios from "axios";
import { getAuthState } from "@/stores/authStore";
const instance = axios.create({
baseURL: "/api",
timeout: 10000,
});
instance.interceptors.request.use((config) => {
const token = getAuthState().token;
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
export default instance;
✅ 注意:我们没有用 **useAuthStore()**
,因为拦截器不是 React 环境,不能使用 Hooks!
🧩 Step 3:React 组件中响应状态
// components/UserProfile.tsx
import React from "react";
import { useAuthUser, useAuthActions } from "@/stores/authStore";
const UserProfile = () => {
const { userId, userName } = useAuthUser();
const { logout } = useAuthActions();
return (
<div>
<p>用户:{userName} (ID: {userId})</p>
<button onClick={logout}>退出登录</button>
</div>
);
};
当 userName
或 userId
发生变化时,组件会自动刷新。
🎯 总结
你要做什么 | 推荐用法 |
---|---|
React 组件内使用 token | useAuthToken() |
React 组件内使用 user 信息 | useAuthUser() |
React 中调用登录/登出方法 | useAuthActions() |
在 axios、工具函数中获取 token | authStore.getState().token 或封装的 getAuthState() |
在外部设置状态 | authStore.setState() 或封装的 setAuthState() |