React 状态管理与网络请求实战笔记:Zustand + Axios 深度解析
在 React 项目开发中,状态管理与网络请求是两大核心模块。 Zustand 作为轻量级状态管理库,以简洁的 API、良好的 TypeScript 支持的优势,逐渐成为 Redux、Context API 的替代方案之一;而 Axios 则是前端网络请求的主流工具,通过拦截器可实现请求统一处理、响应规范化等功能。本文结合实际项目代码,深入剖析 Zustand 状态管理、Axios 拦截器的实战用法,重点解决两者结合使用中的常见问题(如 Hook 调用错误),并梳理核心知识点与最佳实践,助力开发者高效搭建稳定的 React 应用。
一、技术栈核心概述
1.1 Zustand 简介
Zustand 是由 Poimandres(原 React-Spring 团队)开发的轻量级 React 状态管理库,核心特点的是“简洁、高效、无冗余模板”。与 Redux 繁琐的 Action、Reducer、中间件配置不同,Zustand 基于 Hook 设计,无需 Provider 包裹组件树,可直接在组件中通过自定义 Hook 访问和修改状态;同时原生支持 TypeScript,类型推导清晰,能有效避免类型错误。
适用于中小型项目的全局状态管理(如用户信息、权限控制、页面公共状态),也可与局部状态配合使用,平衡开发效率与性能。本文中主要用于管理用户登录状态(token、用户信息)和首页数据状态(轮播图、帖子列表)。
1.2 Axios 简介
Axios 是一款基于 Promise 的 HTTP 客户端,支持浏览器和 Node.js 环境,提供了请求/响应拦截器、请求取消、超时设置、JSON 自动转换等强大功能。在 React 项目中,Axios 常被用于统一管理 API 请求,通过拦截器实现请求头添加(如 Token 认证)、响应数据格式化、错误统一处理等需求,减少重复代码,提升项目可维护性。
1.3 技术结合场景
本文项目中,Zustand 与 Axios 的核心结合点在于:通过 Zustand 存储用户登录后的 Token 信息,Axios 请求拦截器从 Zustand 中获取 Token 并添加到请求头,实现接口的身份认证;同时,Zustand 管理首页帖子列表的加载状态与数据,通过异步方法调用 Axios 请求接口,完成数据获取与状态更新。这一组合覆盖了“状态存储-网络请求-状态同步”的完整业务链路,是 React 项目的典型应用场景。
二、Zustand 状态管理实战
2.1 Zustand 核心 API 解析
Zustand 的 API 极简,核心仅包含 create 方法(创建状态容器),配合中间件(如 persist 持久化存储)可扩展功能。以下结合项目代码拆解核心用法:
2.1.1 create 方法:创建状态容器
create 是 Zustand 的核心方法,用于创建一个状态容器,接收一个函数作为参数,该函数返回状态对象与修改状态的方法(通过 set 函数更新状态)。同时支持 TypeScript 泛型,可定义状态接口,实现类型约束。
语法格式:
import { create } from 'zustand';
interface State {
// 状态字段
count: number;
// 状态修改方法
increment: () => void;
}
// 创建状态容器
export const useCountStore = create<State>((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
}));
其中,set 函数有两种用法:
- 直接传入状态对象:
set({ count: 1 }),适用于无需依赖当前状态的场景; - 传入函数:
set((state) => ({ count: state.count + 1 })),适用于需要基于当前状态计算新状态的场景,确保状态更新的准确性。
2.1.2 persist 中间件:状态持久化
默认情况下,Zustand 管理的状态存储在内存中,页面刷新后会丢失。persist 是 Zustand 官方提供的中间件,可将状态持久化到 localStorage 或 sessionStorage,页面刷新后自动恢复状态,适用于用户信息、登录状态等需要长期保存的数据。
核心配置参数:
name:持久化存储的键名,用于在存储中标识当前状态;partialize:可选参数,用于指定需要持久化的状态字段,避免不必要的数据存储;storage:可选参数,指定存储方式(默认localStorage,可改为sessionStorage)。
2.2 项目状态管理实现
本文项目中,基于 Zustand 实现了两个核心状态容器:useUserStore(用户状态管理)和 useHomeStore(首页数据状态管理),分别对应用户登录模块和首页展示模块。
2.2.1 用户状态管理:useUserStore
该状态容器用于管理用户的登录状态(token、用户信息、是否登录),并提供登录方法,结合 persist 中间件实现状态持久化。以下是代码拆解:
// 导入依赖
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
import { doLogin } from '@/api/user';
import type { User } from '@/types';
import type { Credential } from '@/types';
// 定义状态接口,约束状态字段与方法类型
interface UserStore {
token: string; // 登录凭证 Token
user: User | null; // 用户信息,联合类型(存在/不存在)
isLogin: boolean; // 是否登录标识
login: (credentials: Credential) => Promise<void>; // 登录方法,异步无返回值
}
// 创建用户状态容器,结合 persist 中间件实现持久化
export const useUserStore = create<UserStore>()(
persist(
(set) => ({
// 初始状态
token: "",
user: null,
isLogin: false,
// 登录方法:异步请求接口,更新状态
login: async ({ name, password }) => {
const res = await doLogin({ name, password }); // 调用登录 API
console.log(res, '登录结果');
// 通过 set 函数更新状态
set({
user: res.user,
token: res.token,
isLogin: true,
});
}
}),
{
name: 'user-store', // 持久化存储的键名
// 指定需要持久化的字段,避免冗余数据
partialize: (state) => ({
token: state.token,
user: state.user,
isLogin: state.isLogin,
})
}
)
);
关键知识点解析:
- 类型约束:通过
UserStore接口明确状态字段的类型,login方法接收Credential类型参数(包含用户名、密码),返回 Promise,确保 TypeScript 类型推导准确,减少运行时错误。 - 异步状态更新:登录方法为异步函数,通过
await doLogin等待登录接口响应,获取 Token 和用户信息后,通过set函数更新状态。Zustand 支持异步方法直接写入状态容器,无需额外处理中间件(如 Redux-Thunk),简化异步逻辑。 - 状态持久化:
persist中间件包裹状态容器后,指定name: 'user-store',状态会自动存储到localStorage.getItem('user-store')中;partialize配置仅持久化token、user、isLogin三个字段,避免存储无关数据,优化性能。
2.2.2 首页状态管理:useHomeStore
该状态容器用于管理首页的轮播图数据、帖子列表数据,以及帖子加载方法,实现首页数据的统一管理。代码拆解如下:
import { create } from "zustand";
import type { SlideData } from "@/components/SlideShow";
import type { Post } from "@/types";
import { fetchPosts } from "@/api/posts";
// 定义首页状态接口
interface HomeState {
banners: SlideData[]; // 轮播图数据
posts: Post[]; // 帖子列表数据
loadMore: () => Promise<void>; // 加载更多帖子方法
}
// 创建首页状态容器
export const useHomeStore = create<HomeState>((set) => ({
// 初始轮播图数据
banners: [
{
id: 1,
title: "React 生态系统",
image: "https://images.unsplash.com/photo-1633356122544-f134324a6cee?q=80&w=2070&auto=format&fit=crop",
},
{
id: 2,
title: "移动端开发最佳实践",
image: "https://img.36krcdn.com/hsossms/20260114/v2_1ddcc36679304d3390dd9b8545eaa57f@5091053@ai_oswg1012730oswg1053oswg495_img_png~tplv-1marlgjv7f-ai-v3:600:400:600:400:q70.jpg?x-oss-process=image/format,webp",
},
{
id: 3,
title: "百度上线七猫漫剧,打的什么主意?",
image: "https://img.36krcdn.com/hsossms/20260114/v2_8dc528b02ded4f73b29b7c1019f8963a@5091053@ai_oswg1137571oswg1053oswg495_img_png~tplv-1marlgjv7f-ai-v3:600:400:600:400:q70.jpg?x-oss-process=image/format,webp",
}
],
posts: [], // 初始帖子列表为空
// 加载更多帖子方法
loadMore: async () => {
console.log(await fetchPosts()); // 调用获取帖子 API
}
}));
关键知识点解析:
- 静态数据初始化:轮播图
banners字段初始化为固定数组,适用于无需接口请求的静态数据;帖子列表posts初始化为空数组,等待接口请求后填充数据。 - 状态与 API 结合:
loadMore方法为异步函数,调用fetchPosts接口获取帖子数据,后续可扩展为“获取数据后更新posts状态”(如合并新旧数据),实现无限滚动加载功能。 - 可扩展性:当前代码为基础骨架,可扩展添加
page(当前页码)、isLoading(加载中状态)、hasMore(是否有更多数据)等字段,优化加载逻辑(如防止重复请求、显示加载提示)。
2.3 Zustand 核心踩坑点与解决方案
在使用 Zustand 过程中,最常见的问题是“Hook 调用错误”,尤其是在非 React 组件环境中调用 Zustand 自定义 Hook。以下结合项目报错场景,深入分析原因与解决方案。
2.3.1 核心问题:Hook 执行环境限制
Zustand 提供的 useUserStore、useHomeStore 本质是 React 自定义 Hook,遵循 React Hooks 的核心规则:只能在 React 函数组件或自定义 Hook 的顶层执行,不能在普通函数、if/for 循环、异步代码、Axios 拦截器中调用。
项目中曾出现如下报错:
Uncaught (in promise) Error: Invalid hook call. Hooks can only be called inside of the body of a function component.
at Object.throwInvalidHookError (react-dom_client.js)
at exports.useCallback (chunk-EU35WYTC.js)
at useStore (zustand.js)
at useBoundStore (zustand.js)
at config.ts:10:19
at async Axios.request (axios.js)
报错根源:在 Axios 请求拦截器(普通 JavaScript 函数,脱离 React 组件渲染流程)中直接调用 useUserStore() 自定义 Hook,违反了 Hooks 使用规则。
2.3.2 解决方案:getState() 方法
Zustand 为非 React 环境(如普通函数、拦截器、工具函数)提供了 getState() 方法,该方法并非 Hook,无 React 环境限制,可在任意位置调用,用于获取当前状态的快照。
核心特点:
- 无环境限制:不属于 React Hook,可在 Axios 拦截器、普通工具函数、异步代码中自由调用;
- 实时获取状态:每次调用都会获取最新的状态值,适用于 Token 刷新、状态动态变化的场景;
- 同步执行:无需异步等待,不影响原有代码流程(如 Axios 拦截器的执行效率)。
项目中修正后的 Axios 拦截器代码如下(后续章节详细解析):
// 在 Axios 请求拦截器中使用 getState() 获取 Token
axios.interceptors.request.use(config => {
const token = useUserStore.getState().token; // 非 Hook 调用,无环境限制
if(token){
config.headers.Authorization = `Bearer ${token}`;
}
return config
});
2.3.3 补充说明:getState() 与 Hook 调用的区别
| 调用方式 | 适用场景 | 是否响应式 | 环境限制 |
|---|---|---|---|
useUserStore()(Hook 调用) | React 函数组件/自定义 Hook | 是,状态变化时组件自动重渲染 | 有,仅能在组件顶层调用 |
useUserStore.getState()(非 Hook 调用) | 普通函数、拦截器、工具函数 | 否,仅获取当前快照,需手动监听变化 | 无,可在任意位置调用 |
总结:在 React 组件中,优先使用 Hook 调用方式(响应式更新);在非 React 环境中,使用 getState() 方法获取状态。
三、Axios 网络请求实战
3.1 Axios 基础配置
项目中通过单独的 config.ts 文件对 Axios 进行全局配置,包括基础路径、请求拦截器、响应拦截器,实现请求/响应的统一处理。基础配置代码如下:
import axios from 'axios';
import { useUserStore } from '@/store/useUserStore';
// 配置基础路径(区分开发环境:Mock 服务/后端服务)
axios.defaults.baseURL = 'http://localhost:5173/api'; // 前端 Mock 地址
// axios.defaults.baseURL = 'http://localhost:3000/api'; // 后端服务地址
// 请求拦截器:请求发送前处理
axios.interceptors.request.use(config => {
// 从 Zustand 获取 Token,添加到请求头
const token = useUserStore.getState().token;
if(token){
config.headers.Authorization = `Bearer ${token}`;
}
return config
});
// 响应拦截器:响应接收后处理
axios.interceptors.response.use(res => {
console.log('////');
if(res.status != 200){
console.log(res,'响应错误');
return;
}
return res.data; // 统一返回响应数据,简化组件中数据获取逻辑
})
export default axios;
3.2 拦截器核心功能解析
3.2.1 请求拦截器
请求拦截器通过 axios.interceptors.request.use() 注册,在请求发送到服务器之前执行,核心作用包括:
- 添加请求头:如添加 Token 认证信息(
Authorization字段)、设置 Content-Type(如 JSON 格式); - 请求参数格式化:如统一处理日期格式、参数序列化;
- 添加公共参数:如所有请求携带用户 ID、设备信息等;
- 请求加载状态控制:如设置全局加载中状态(显示 Loading 组件)。
项目中请求拦截器的核心逻辑是“从 Zustand 获取 Token 并添加到请求头”,实现接口的身份认证。需要注意的是,此处必须使用 getState() 方法而非 Hook 调用,避免触发无效 Hook 错误。
3.2.2 响应拦截器
响应拦截器通过 axios.interceptors.response.use() 注册,在组件接收响应数据之前执行,核心作用包括:
- 响应数据规范化:如统一返回
res.data,避免组件中重复书写response.data; - 错误统一处理:如拦截 401(未授权)、403(权限不足)、500(服务器错误)等状态码,执行对应逻辑(如 401 跳转登录页);
- 加载状态关闭:如请求完成后关闭全局 Loading 组件;
- 数据转换:如将后端返回的时间戳转换为日期格式。
项目中响应拦截器的逻辑较为基础:判断响应状态码是否为 200,非 200 时打印错误信息;200 时返回 res.data,简化组件中数据处理流程。可扩展的优化点:
// 优化后的响应拦截器
axios.interceptors.response.use(
res => {
return res.data;
},
(error) => {
// 错误统一处理
const status = error.response?.status;
switch(status){
case 401:
// Token 过期或未登录,跳转登录页
window.location.href = '/login';
break;
case 403:
// 权限不足,提示用户
alert('无权限访问该资源');
break;
case 500:
// 服务器错误,提示用户
alert('服务器异常,请稍后重试');
break;
default:
console.error('请求错误:', error.message);
}
return Promise.reject(error);
}
);
3.3 API 接口封装
项目中通过单独的 API 文件夹封装接口请求(如 posts.ts),将网络请求逻辑与组件分离,提升代码可维护性。以获取帖子列表接口为例,代码如下:
import axios from './config';
import type { Post } from '@/types';
/**
* 获取帖子列表
* @param page 页码,默认1
* @param limit 每页条数,默认10
* @returns 帖子列表数据
*/
export const fetchPosts = async (
page: number = 1,
limit: number = 10,
) => {
try{
const response = await axios.get('/posts',{
params:{
page,
limit,
}
})
console.log('获取帖子列表成功',response);
return response;
}catch(error){
console.error('获取帖子列表失败',error);
throw error; // 抛出错误,让调用方处理
}
};
接口封装核心原则:
- 类型约束:通过 TypeScript 定义参数类型(
page、limit)和返回值类型(Post数组),确保类型安全; - 参数默认值:为页码、每页条数设置默认值(
page=1、limit=10),简化调用; - 错误处理:使用
try/catch捕获请求错误,打印错误信息并抛出错误,让调用方(如组件、状态方法)根据业务需求处理错误(如显示错误提示); - 职责单一:每个函数仅对应一个 API 接口,避免逻辑冗余,便于后续维护和修改。
四、Zustand 与 Axios 结合实战:完整业务链路
本节结合项目代码,梳理“用户登录-获取 Token-请求帖子列表-更新状态”的完整业务链路,展示 Zustand 与 Axios 的协同工作流程,同时优化首页加载更多逻辑。
4.1 完整业务流程拆解
- 用户登录:用户在登录页输入用户名和密码,调用
useUserStore.login()方法,该方法通过 Axios 调用登录接口(doLogin),获取 Token 和用户信息后,更新 Zustand 状态并持久化到localStorage; - 请求帖子列表:用户进入首页,调用
useHomeStore.loadMore()方法,该方法调用fetchPosts接口; - 请求拦截器处理:Axios 请求拦截器通过
useUserStore.getState().token获取 Token,添加到请求头Authorization中,发送请求到服务器; - 响应拦截器处理:服务器验证 Token 有效后,返回帖子列表数据,响应拦截器提取
res.data并返回; - 状态更新:
loadMore方法获取帖子数据后,通过set函数更新useHomeStore中的posts状态,首页组件监听状态变化并重新渲染,展示帖子列表。
4.2 优化首页加载更多逻辑
当前 useHomeStore 中的 loadMore 方法仅调用接口,未更新状态。以下优化代码,实现“加载更多-合并数据-防止重复请求”的完整逻辑:
import { create } from "zustand";
import type { SlideData } from "@/components/SlideShow";
import type { Post } from "@/types";
import { fetchPosts } from "@/api/posts";
interface HomeState {
banners: SlideData[];
posts: Post[];
page: number; // 当前页码
isLoading: boolean; // 加载中状态
hasMore: boolean; // 是否有更多数据
loadMore: () => Promise<void>;
}
export const useHomeStore = create<HomeState>((set, get) => ({
banners: [
// 轮播图数据(略)
],
posts: [],
page: 1,
isLoading: false,
hasMore: true,
loadMore: async () => {
const { page, isLoading, hasMore } = get(); // 获取当前状态
// 防止重复请求、无更多数据时停止请求
if (isLoading || !hasMore) return;
set({ isLoading: true }); // 设置加载中状态
try {
const data = await fetchPosts(page, 10); // 调用接口,传入页码和条数
// 判断是否有更多数据(假设后端返回 total 字段)
const newHasMore = data.total > page * 10;
// 合并新旧帖子数据,更新状态
set({
posts: prev => [...prev, ...data.list], // 合并数组,保留原有数据
page: page + 1, // 页码自增
hasMore: newHasMore,
isLoading: false, // 关闭加载中状态
});
} catch (error) {
console.error('加载更多帖子失败', error);
set({ isLoading: false }); // 加载失败也关闭加载中状态
}
}
}));
优化点解析:
- 添加状态字段:新增
page(当前页码)、isLoading(加载中状态)、hasMore(是否有更多数据),完善加载逻辑; - 防止重复请求:通过
if (isLoading || !hasMore) return阻止加载中或无更多数据时的重复请求; - 状态联动更新:使用
get()方法获取当前状态(页码、加载状态),请求成功后合并新旧帖子数据([...prev, ...data.list]),更新页码和是否有更多数据的状态; - 异常处理优化:加载失败时关闭加载中状态,避免 Loading 组件一直显示,提升用户体验。
4.3 组件中使用状态与接口
在首页组件中,通过 Zustand Hook 访问状态和方法,实现数据展示与加载更多功能:
import { useHomeStore } from '@/store/useHomeStore';
import SlideShow from '@/components/SlideShow';
const Home = () => {
// 从 Zustand 获取状态和方法(Hook 调用,响应式更新)
const { banners, posts, isLoading, hasMore, loadMore } = useHomeStore();
// 滚动到底部触发加载更多
const handleScroll = () => {
const scrollTop = document.documentElement.scrollTop;
const scrollHeight = document.documentElement.scrollHeight;
const clientHeight = document.documentElement.clientHeight;
if (scrollTop + clientHeight >= scrollHeight - 100 && !isLoading && hasMore) {
loadMore(); // 触发加载更多
}
};
return (
<div onScroll={handleScroll} style={{ height: '100vh', overflow: 'auto' }}>
{/* 轮播图组件 */}
<SlideShow banners={banners} />
{/* 帖子列表 */}
<div className="post-list">
{posts.map(post => (
<div key={post.id} className="post-item">
<h3>{post.title}</h3>
<p>{post.content}</p>
</div>
))}
</div>
{/* 加载提示 */}
{isLoading && <div className="loading">加载中...</div>}
{/* 无更多数据提示 */}
{!hasMore && !isLoading && <div className="no-more">没有更多帖子了</div>}
</div>
);
};
export default Home;
组件中通过 useHomeStore() Hook 获取状态和方法,当 posts、isLoading 等状态变化时,组件会自动重渲染,展示最新数据;同时监听滚动事件,滚动到底部时触发 loadMore 方法,实现无限滚动加载。
五、常见问题与进阶优化
5.1 常见问题汇总与解决方案
5.1.1 问题1:Token 过期导致请求失败
现象:用户长时间未操作,Token 过期,调用接口时返回 401 状态码。
解决方案:在 Axios 响应拦截器中拦截 401 状态码,执行 Token 刷新逻辑或跳转登录页:
// 响应拦截器中添加 401 处理
axios.interceptors.response.use(
res => res.data,
async (error) => {
const status = error.response?.status;
if (status === 401) {
const store = useUserStore.getState();
// 若有刷新 Token 的接口,可在此调用
// const newToken = await refreshToken(store.refreshToken);
// 刷新成功后更新 Token
// useUserStore.setState({ token: newToken });
// 重试原请求
// return axios(error.config);
// 无刷新接口时,跳转登录页,清除状态
store.setState({ token: '', user: null, isLogin: false });
window.location.href = '/login';
}
return Promise.reject(error);
}
);
5.1.2 问题2:Zustand 状态持久化后,TypeScript 类型报错
现象:使用 persist 中间件后,TypeScript 提示状态类型不匹配。
解决方案:确保 partialize 配置中指定的字段与状态接口一致,且初始状态类型正确。若仍报错,可通过泛型明确持久化类型:
import { create } from 'zustand';
import { persist, PersistMiddleware } from 'zustand/middleware';
// 明确持久化中间件的类型
type UserPersistMiddleware = PersistMiddleware<UserStore, UserStore>;
export const useUserStore = create<UserStore>()(
persist<UserStore, UserPersistMiddleware>(
(set) => ({ /* 状态定义 */ }),
{ name: 'user-store' }
)
);
5.1.3 问题3:Axios 重复请求导致数据错乱
现象:快速滚动到底部时,多次触发loadMore 方法,导致多个请求同时发送,返回数据顺序错乱。
解决方案:通过 isLoading 状态控制,加载中时禁止重复请求(已在 4.2 节优化中实现),同时可使用 Axios 取消请求功能,取消前一个未完成的请求:
// 在 useHomeStore 中添加取消请求的控制器
import { CancelTokenSource } from 'axios';
interface HomeState {
// 其他状态...
cancelSource: CancelTokenSource | null;
}
export const useHomeStore = create<HomeState>((set, get) => ({
// 其他初始状态...
cancelSource: null,
loadMore: async () => {
const { page, isLoading, hasMore, cancelSource } = get();
if (isLoading || !hasMore) return;
// 取消前一个未完成的请求
if (cancelSource) cancelSource.cancel('请求被取消');
// 创建新的取消控制器
const newCancelSource = axios.CancelToken.source();
set({ isLoading: true, cancelSource: newCancelSource });
try {
const data = await fetchPosts(page, 10, newCancelSource);
// 状态更新逻辑(略)
} catch (error) {
if (axios.isCancel(error)) {
console.log('请求被取消:', error.message);
} else {
console.error('加载失败:', error);
}
} finally {
set({ isLoading: false });
}
}
}));
5.2 进阶优化建议
5.2.1 Zustand 状态分片与选择器
当状态容器较大时,可通过选择器(Selector)仅订阅需要的状态字段,避免组件不必要的重渲染:
// 组件中仅订阅 posts 和 isLoading 字段
const { posts, isLoading } = useHomeStore(
(state) => ({ posts: state.posts, isLoading: state.isLoading })
);
同时,可将大型状态容器拆分为多个小容器(如用户状态、首页状态、设置状态),提升代码可读性和维护性。
5.2.2 Axios 请求缓存
对于不常变化的数据(如轮播图、分类列表),可添加请求缓存功能,减少重复请求,提升性能。可使用 axios-cache-adapter 插件,或手动实现缓存逻辑:
// 手动实现简单缓存
const cache = new Map();
export const fetchBanners = async () => {
// 检查缓存,存在则直接返回
if (cache.has('banners')) {
return cache.get('banners');
}
const data = await axios.get('/banners');
cache.set('banners', data); // 存入缓存
return data;
};
5.2.3 环境变量区分基础路径
项目开发中,需区分开发环境、测试环境、生产环境的 API 基础路径,可通过环境变量实现:
// 根据环境变量设置基础路径
if (import.meta.env.MODE === 'development') {
axios.defaults.baseURL = import.meta.env.VITE_API_BASE_URL_DEV;
} else if (import.meta.env.MODE === 'production') {
axios.defaults.baseURL = import.meta.env.VITE_API_BASE_URL_PROD;
}
在 .env.development 和 .env.production 文件中配置对应的环境变量,避免手动修改代码。
六、总结与展望
6.1 核心知识点总结
本文围绕 Zustand 状态管理与 Axios 网络请求,结合项目实战代码,梳理了以下核心知识点:
- Zustand 用法:通过
create方法创建状态容器,persist中间件实现状态持久化,getState()方法在非 React 环境中获取状态,遵循 React Hooks 规则避免调用错误; - Axios 配置:通过请求/响应拦截器实现 Token 添加、错误统一处理、数据规范化,接口封装遵循类型约束和职责单一原则;
- 两者结合:Zustand 存储全局状态(Token、用户信息、页面数据),Axios 负责网络请求,通过
getState()方法实现状态与请求的协同,构建完整业务链路; - 问题解决:针对 Hook 调用错误、Token 过期、重复请求等常见问题,提供了具体的解决方案和优化思路。
6.2 技术展望
Zustand 作为轻量级状态管理库,在中小型项目中具有明显的开发效率优势,未来可结合 React 18 的 Concurrent Mode、Server Components 等新特性,进一步优化状态更新性能;Axios 可结合请求取消、重试机制、缓存策略等,构建更健壮的网络请求层。
在实际项目开发中,需根据项目规模和需求选择合适的技术方案:中小型项目可采用“Zustand + Axios”组合,兼顾开发效率与性能;大型项目可结合 React Query 处理服务端状态,进一步分离本地状态与服务端状态,提升项目可维护性。
通过本文的学习与实践,可熟练掌握 Zustand 与 Axios 的核心用法,避开常见坑点,高效搭建稳定、可扩展的 React 应用。后续需持续关注技术更新,结合实际业务场景不断优化代码,提升开发能力。