作为前端新手,你是不是也遇到过这些崩溃瞬间?—— 组件 A 的状态想传给组件 B,得先传给父组件再转发;页面刷新后,好不容易填的表单数据全没了;用 Redux 写了一堆模板代码,最后发现就为了管理一个计数器… 😭
今天咱们就来拯救这些 “状态焦虑”—— 聊聊 Zustand,这个能让你摆脱冗余代码、轻松管理全局状态的神器!
前端状态管理的那些事儿 🤔
现代前端开发就像搭积木:UI 组件是一块块积木,而全局应用状态就是把积木粘起来的胶水。没有胶水,积木散一地;胶水太黏(比如状态管理太复杂),想调整积木又费劲。
以前咱们可能会用:
useState:管组件自己的状态还行,跨组件传值就歇菜useContext + useReducer:能全局管理了,但得套一层又一层 Provider,代码像裹棉被- Redux:功能强但模板多,新手看了直呼 “劝退”
而 Zustand 就不一样了 —— 它轻得像羽毛(体积小),用起来像useState(hooks 风格),还能管全局状态。简直是为 React 量身定做的 “状态小管家”!
为什么是 Zustand?选它的 N 个理由 ✨
(一)Zustand 初印象
Zustand(发音类似 “Zustand”,德语里 “状态” 的意思)是个轻量级、hooks 化的 React 状态管理库。核心优势就俩字:简单。
不管是中小型项目(比如个人博客、管理后台),还是大型项目(复杂表单、多页面数据共享),它都能 hold 住。新手看一眼 API,基本就能上手,这不比对着 Redux 文档发呆香?
(二)和其他库的 battle:为啥不选别人?
咱们直接上对比表,一目了然:
| 方案 | 优点 | 缺点 | 适合场景 |
|---|---|---|---|
| useState | 简单、原生 | 不能跨组件共享 | 单个组件内部状态 |
| Context + Reducer | 原生支持、能全局管理 | 需要 Provider 嵌套、性能一般 | 小型项目简单全局状态 |
| Redux | 生态完善、规范 | 模板多、学习成本高 | 超大型项目、需要严格规范 |
| Zustand | 无需 Provider、API 简单 | 生态不如 Redux 完善 | 中小项目、追求开发效率 |
简单说:小项目如果组件少,useState足够;但只要需要跨组件共享状态(比如购物车数据、用户登录状态),Zustand 就是性价比之王!
实战开始!Zustand 基本操作(超简单,别慌) 🙌
(一)安装:一行命令搞定
先给项目装个 Zustand,打开终端输入:
npm install zustand
等几秒钟,搞定!比泡杯速溶咖啡还快 ☕
(二)创建你的第一个 Store:从计数器开始
Store 就是 “状态仓库”,所有全局状态都存在这里。咱们先从最简单的计数器开始,创建一个能存count的 Store。
// src/store/count.js
import { create } from "zustand";
// 创建Store:用create函数,返回一个自定义hook(useCounterStore)
export const useCounterStore = create((set, get) => ({
// 1. 定义状态:初始count为0
count: 0,
// 2. 定义修改状态的方法:加1
increment: () => set((state) => ({ count: state.count + 1 })),
// 3. 定义修改状态的方法:减1
decrement: () => set((state) => ({ count: state.count - 1 })),
// 4. 获取当前状态的方法(可选,直接用get()获取)
getCount: () => get().count,
}));
【代码解释】:
create:Zustand 的核心函数,用来创建 Store,参数是一个函数set:修改状态的工具,调用后会触发组件重新渲染(类似setState)get:获取当前状态的工具(比如get().count就能拿到当前 count 值)- 最后返回的
useCounterStore是个自定义 hook,之后在组件里直接用!
(三)在组件中使用 Store:让计数器跑起来
Store 创建好了,现在让组件 “连接” 它,显示 count 并能修改。
// src/components/Counter/index.jsx
import { useCounterStore } from "../../store/count";
const Counter = () => {
// 从Store里“取”出需要的状态和方法
const { count, increment, decrement } = useCounterStore();
return (
<div>
<h1>当前计数 ❤️:{count}</h1>
{/* 点击按钮调用increment,count会+1 */}
<button onClick={increment}>加1</button>
{/* 点击按钮调用decrement,count会-1 */}
<button onClick={decrement}>减1</button>
</div>
);
};
export default Counter;
【使用说明】:
-
直接导入咱们创建的
useCounterStore(不用包 Provider,爽!) -
像解构对象一样拿出
count(状态)和increment(方法) -
按钮点击时调用方法,状态自动更新,组件自动重新渲染 —— 和用
useState几乎一样!
现在把这个组件放进App.js,运行项目,点击按钮试试?是不是已经能正常计数了?
实战案例大放送:3 个场景吃透 Zustand 🚀
光会计数器还不够,咱们来三个实战场景,从简单到复杂,彻底学会用 Zustand 解决实际问题。
(一)计数器进阶:多组件共享状态
刚才的计数器只能在一个组件用?太浪费了!Zustand 的核心能力是全局共享—— 让多个组件用同一个 count。
比如在App.js里也显示 count,并且能修改:
// src/App.jsx
import { useCounterStore } from "./store/count";
import Counter from "./components/Counter";
function App() {
// 同样导入useCounterStore,取状态和方法
const { count, increment, decrement } = useCounterStore();
return (
<div>
{/* App组件里的计数器 */}
<h1>App里的计数 💖:{count}</h1>
<button onClick={increment}>App加1</button>
<button onClick={decrement}>App减1</button>
{/* 引入之前写的Counter组件 */}
<Counter />
</div>
);
}
export default App;
【效果演示】:
- 点击 App 里的 “加 1”,App 和 Counter 组件的 count 会一起增加
- 点击 Counter 里的 “减 1”,两个地方的 count 会一起减少
这就是全局状态的魔力!不管在哪个组件修改,所有用了这个状态的组件都会同步更新 —— 再也不用手动传 props 了 🎉
(二)TodoList:状态管理的经典场景
TodoList 需要增删改查任务,状态比较多(任务列表、操作方法),用 Zustand 管理再合适不过。
第一步:创建 TodoList 的 Store
// src/store/todos.js
import { create } from "zustand";
export const useTodosStore = create((set) => ({
// 初始任务列表
todos: [
{ id: 1, text: "学会Zustand", completed: false },
{ id: 2, text: "写一篇博客", completed: false },
],
// 1. 添加任务
addTodo: (text) => set((state) => ({
todos: [
...state.todos, // 保留原来的任务
// 新增任务(id用长度+1,简单处理)
{ id: state.todos.length + 1, text, completed: false }
]
})),
// 2. 删除任务(根据id)
deleteTodo: (id) => set((state) => ({
todos: state.todos.filter(todo => todo.id !== id) // 过滤掉要删除的id
})),
// 3. 切换任务完成状态(根据id)
toggleTodo: (id) => set((state) => ({
todos: state.todos.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
)
}))
}));
【Store 说明】:
- 状态
todos是任务数组,每个任务有id、text(内容)、completed(是否完成) - 三个方法分别对应 “增”(addTodo)、“删”(deleteTodo)、“改”(toggleTodo)
- 都用
set函数更新状态 ——Zustand 推荐用这种 “不可变更新” 的方式(不直接修改原数组,而是返回新数组)
第二步:创建 TodoList 组件
// src/components/TodoList/index.jsx
import { useTodosStore } from "../../store/todos";
import { useState } from "react"; // 用useState存输入框内容
const TodoList = () => {
// 从Store取状态和方法
const { todos, addTodo, deleteTodo, toggleTodo } = useTodosStore();
// 本地状态:输入框内容(不需要全局共享的状态,还用useState)
const [inputText, setInputText] = useState("");
// 处理添加任务
const handleAdd = () => {
if (!inputText.trim()) return; // 空内容不添加
addTodo(inputText); // 调用Store的addTodo方法
setInputText(""); // 清空输入框
};
return (
<div>
<h1>Todo List 📝</h1>
{/* 输入框和添加按钮 */}
<div>
<input
type="text"
value={inputText}
onChange={(e) => setInputText(e.target.value)}
placeholder="输入任务..."
/>
<button onClick={handleAdd}>添加任务</button>
</div>
{/* 任务列表 */}
<ul>
{todos.map((todo) => (
<li key={todo.id} style={{ textDecoration: todo.completed ? "line-through" : "none" }}>
{/* 复选框切换完成状态 */}
<input
type="checkbox"
checked={todo.completed}
onChange={() => toggleTodo(todo.id)}
/>
{todo.text}
{/* 删除按钮 */}
<button onClick={() => deleteTodo(todo.id)}>删除</button>
</li>
))}
</ul>
</div>
);
};
export default TodoList;
【核心亮点】:
- 全局状态(todos)用 Zustand 管理,本地状态(inputText)还用
useState—— 分工明确! - 添加任务时,调用
addTodo传入输入框内容,Store 自动更新,列表自动刷新 - 删除和切换状态同理,只需要调用 Store 的方法,不用关心状态怎么传
现在把TodoList组件放进App.js,试试添加、删除、勾选任务 —— 是不是已经能正常工作了?
(三)Github 仓库列表:异步请求 + 状态管理
实际项目中,我们经常需要请求接口(比如获取后端数据),这时候需要管理 “加载中”“请求失败” 等状态。Zustand 也能轻松搞定!
咱们做一个 “获取 Github 用户仓库列表” 的功能,包含:
- 加载状态(loading)
- 错误状态(error)
- 仓库数据(repos)
第一步:准备接口请求(用 axios)
先装 axios:npm install axios,然后创建接口文件:
// src/api/config.js
import axios from "axios";
// 设置基础URL(Github API的基础地址)
axios.defaults.baseURL = "https://api.github.com";
export default axios;
// src/api/repo.js
import axios from "./config";
// 获取用户的仓库列表(比如获取"WildBlue58"这个用户的仓库)
export const getRepoList = async (owner) => {
// 调用Github API:/users/{用户名}/repos
const response = await axios.get(`/users/${owner}/repos`);
return response.data; // 返回仓库数据
};
第二步:创建仓库列表的 Store(管理异步状态)
// src/store/repos.js
import { create } from "zustand";
import { getRepoList } from "../api/repo";
export const useReposStore = create((set) => ({
repos: [], // 仓库列表数据
loading: false, // 加载状态(true表示正在请求)
error: null, // 错误信息(null表示无错误)
// 异步获取仓库列表的方法
fetchRepos: async (owner) => {
// 1. 开始请求:设置loading为true,清空错误
set({ loading: true, error: null });
try {
// 2. 调用接口获取数据
const data = await getRepoList(owner);
// 3. 请求成功:存数据,结束loading
set({ repos: data, loading: false });
} catch (err) {
// 4. 请求失败:存错误信息,结束loading
set({ error: err.message, loading: false });
}
},
}));
【异步状态管理要点】:
- 除了数据(repos),还定义了
loading和error状态 —— 这三个状态会一起被管理 fetchRepos是异步方法(用async/await),调用接口的整个流程都在 Store 里封装- 请求前:
loading: true(显示 “加载中”) - 请求成功:存数据
repos: data,loading: false - 请求失败:存错误
error: err.message,loading: false
第三步:创建仓库列表组件
// src/components/RepoList/index.jsx
import { useReposStore } from "../../store/repos";
import { useEffect } from "react";
const RepoList = () => {
// 从Store取状态和方法
const { repos, loading, error, fetchRepos } = useReposStore();
// 组件挂载时,请求"WildBlue58"的仓库列表
useEffect(() => {
fetchRepos("WildBlue58");
}, [fetchRepos]); // 依赖项加fetchRepos(虽然它不会变,但规范起见)
// 加载中显示
if (loading) return <p>加载中... 🌀</p>;
// 出错显示
if (error) return <p>出错了:{error} ❌</p>;
return (
<div>
<h1>Github仓库列表 🐱</h1>
<ul>
{repos.map((repo) => (
<li key={repo.id} style={{ margin: "10px 0" }}>
{/* 仓库名称(链接到仓库页面) */}
<a
href={repo.html_url}
target="_blank"
rel="noopener noreferrer"
style={{ fontSize: "18px", color: "#0366d6" }}
>
{repo.name}
</a>
{/* 仓库描述 */}
<p>{repo.description || "暂无描述"}</p>
</li>
))}
</ul>
</div>
);
};
export default RepoList;
【使用逻辑】:
-
组件一加载(
useEffect),就调用fetchRepos请求数据 -
根据
loading显示 “加载中”,根据error显示错误信息 -
请求成功后,遍历
repos显示仓库列表
把这个组件放进App.js,运行后会看到:先显示 “加载中”,然后加载出仓库列表(如果网络没问题的话)。如果把用户名改成一个不存在的(比如 “abc123456789”),会显示错误信息 —— 这就是完整的异步状态管理流程!
总结:Zustand 值得拥有吗? 🎯
经过三个案例的实战,你应该能感受到 Zustand 的优点了:
- 简单易用:不用写 Provider,不用记复杂 API,像
useState一样自然 - 功能全面:能管同步状态(计数器、Todo),也能管异步状态(接口请求)
- 灵活高效:全局状态和本地状态分工明确,性能也不错
- 学习成本低:新手花 1 小时就能上手,比 Redux 友好太多
当然,它也不是万能的 —— 如果你的项目需要非常复杂的中间件、时间旅行调试,Redux 生态可能更合适。但对于 90% 的前端项目(尤其是中小项目),Zustand 绝对是 “够用又省心” 的选择。
现在就去你的项目里试试吧!从一个简单的全局状态(比如用户登录状态)开始,慢慢体会 Zustand 的便捷 —— 相信我,用过之后你会回来感谢我的 😉