Appearance
Zustand 状态管理
代码极少;不需要 Provider;简洁度高;性能好;轻量,灵活,适合中小型到大型项目
安装
bash
npm install zustandnpm install zustand创建一个 Store
js
// src/stores/counterStore.js
import { create } from "zustand";
// create() 函数接收一个回调,回调的参数是 set 函数
// set 函数是唯一用来修改状态的工具
const useCounterStore = create((set) => ({
// 1. 定义 State
count: 0,
posts: [],
loading: false,
// 2. 定义 "Actions" (它们就是普通的方法)
increment: () => {
// 调用 set 函数来更新状态
set((state) => ({ count: state.count + 1 }));
},
decrement: () => {
set((state) => ({ count: state.count - 1 }));
},
incrementByAmount: (amount) => {
set((state) => ({ count: state.count + amount }));
},
// 3. 定义异步 Action
fetchPosts: async () => {
set({ loading: true }); // 进入加载状态
try {
const response = await fetch(
"https://jsonplaceholder.typicode.com/posts"
);
const data = await response.json();
set({ posts: data.slice(0, 5), loading: false }); // 更新数据,结束加载
} catch (error) {
console.error("获取数据失败", error);
set({ loading: false }); // 结束加载
}
},
}));
export default useCounterStore;// src/stores/counterStore.js
import { create } from "zustand";
// create() 函数接收一个回调,回调的参数是 set 函数
// set 函数是唯一用来修改状态的工具
const useCounterStore = create((set) => ({
// 1. 定义 State
count: 0,
posts: [],
loading: false,
// 2. 定义 "Actions" (它们就是普通的方法)
increment: () => {
// 调用 set 函数来更新状态
set((state) => ({ count: state.count + 1 }));
},
decrement: () => {
set((state) => ({ count: state.count - 1 }));
},
incrementByAmount: (amount) => {
set((state) => ({ count: state.count + amount }));
},
// 3. 定义异步 Action
fetchPosts: async () => {
set({ loading: true }); // 进入加载状态
try {
const response = await fetch(
"https://jsonplaceholder.typicode.com/posts"
);
const data = await response.json();
set({ posts: data.slice(0, 5), loading: false }); // 更新数据,结束加载
} catch (error) {
console.error("获取数据失败", error);
set({ loading: false }); // 结束加载
}
},
}));
export default useCounterStore;
- create 函数创建了一个 Hook (useCounterStore)。
- set 函数是核心。当你调用它时,Zustand 会智能地将你传入的对象合并到现有状态中(shallow merge),类似于 this.setState。
- 你可以在 store 中直接定义异步函数,并在其中多次调用 set 来更新不同阶段的状态(例如,开始加载 -> 加载完成)。
在组件中使用 Store
js
// src/components/Counter.js
import React, { useEffect } from "react";
import useCounterStore from "../stores/counterStore";
function Counter() {
// --- 方法一:推荐!使用选择器(selector)来获取你需要的特定状态 ---
// 这样做的好处是,只有当 `count` 这个值变化时,组件才会重新渲染。
const count = useCounterStore((state) => state.count);
const { increment, decrement, incrementByAmount } = useCounterStore();
// --- 方法二:获取整个 store (当 store 中任何数据变化时都会重渲染) ---
// const { count, increment, decrement, incrementByAmount } = useCounterStore();
return (
<div>
<h2>计数器 (Zustand)</h2>
<span>{count}</span>
<div>
<button onClick={increment}>增加</button>
<button onClick={decrement}>减少</button>
<button onClick={() => incrementByAmount(5)}>增加 5</button>
</div>
</div>
);
}
function PostList() {
const { posts, loading, fetchPosts } = useCounterStore();
// 组件挂载时自动获取数据
useEffect(() => {
fetchPosts();
}, [fetchPosts]); // fetchPosts 函数引用是稳定的,所以这只运行一次
if (loading) {
return <p>正在加载文章...</p>;
}
return (
<div>
<h2>文章列表 (Zustand)</h2>
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</div>
);
}
// 在 App.js 中组合起来
export default function App() {
return (
<div>
<Counter />
<hr />
<PostList />
</div>
);
}// src/components/Counter.js
import React, { useEffect } from "react";
import useCounterStore from "../stores/counterStore";
function Counter() {
// --- 方法一:推荐!使用选择器(selector)来获取你需要的特定状态 ---
// 这样做的好处是,只有当 `count` 这个值变化时,组件才会重新渲染。
const count = useCounterStore((state) => state.count);
const { increment, decrement, incrementByAmount } = useCounterStore();
// --- 方法二:获取整个 store (当 store 中任何数据变化时都会重渲染) ---
// const { count, increment, decrement, incrementByAmount } = useCounterStore();
return (
<div>
<h2>计数器 (Zustand)</h2>
<span>{count}</span>
<div>
<button onClick={increment}>增加</button>
<button onClick={decrement}>减少</button>
<button onClick={() => incrementByAmount(5)}>增加 5</button>
</div>
</div>
);
}
function PostList() {
const { posts, loading, fetchPosts } = useCounterStore();
// 组件挂载时自动获取数据
useEffect(() => {
fetchPosts();
}, [fetchPosts]); // fetchPosts 函数引用是稳定的,所以这只运行一次
if (loading) {
return <p>正在加载文章...</p>;
}
return (
<div>
<h2>文章列表 (Zustand)</h2>
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</div>
);
}
// 在 App.js 中组合起来
export default function App() {
return (
<div>
<Counter />
<hr />
<PostList />
</div>
);
}
小洛的前端技术博客