关键字
Zustand
安装
1
| cnpm install --save zustand
|
使用
API
1 2 3 4
| const useStore = create((set, get, store) => ({ stateKey: initialValue, actionMethod: () => set(...), }));
|
create
接受一个回调函数作为参数,该回调函数会返回一个 状态对象(store),这个状态对象可以包含:
- 状态(state):数据,例如
count: 0
- 方法(actions):用于更新状态的函数,例如
increment: () => set((state) => ({ count: state.count + 1 }))
回调函数的参数
create
的回调函数会接收 三个参数:
1 2 3 4 5
| const useStore = create((set, get, store) => ({ count: 0, increment: () => set((state) => ({ count: state.count + 1 })), getCount: () => get().count, }));
|
参数 |
作用 |
set |
修改状态(类似 setState ) |
get |
获取当前状态 |
store |
访问整个 store API(很少用) |
useStore的返回值
create
返回的是一个 React Hook:
1 2 3 4
| const useStore = create((set) => ({ count: 0, increment: () => set((state) => ({ count: state.count + 1 })), }));
|
你可以在组件中这样使用:
1 2 3 4 5 6 7 8 9
| function Counter() { const { count, increment } = useStore(); return ( <div> <h1>{count}</h1> <button onClick={increment}>增加</button> </div> ); }
|
✅ 特点:
useStore()
👉 订阅整个 store
useStore((state) => state.count)
👉 只订阅 count
,提升性能
store 里定义全部的 state,然后在组件里选出一部分来用。
这个叫做 selector:

状态变了之后,zustand 会对比 selector 出的状态的新旧值,变了才会触发组件重新渲染。
此外,这个 selector 还可以起到派生状态的作用,对原始状态做一些修改:

中间件
其实中间件并不是 zustand 自己实现的功能。
你看这个 create 方法的参数,它是一个接受 set、get、store 的三个参数的函数:

那我们可不可以包一层,自己拿到 get、set、store,对这些做一些修改,之后返回一个接受三个参数的函数呢?
比如这样:
1 2 3 4 5 6 7 8 9 10 11
| function logMiddleware(func) { return function(set, get, store) {
function newSet(...args) { console.log('调用了 set,新的 state:', get()); return set(...args) } return func(newSet, get, store) } }
|
我接受之前的函数,然后对把 set、get、store 修改之后再调用它:

这样不就给 zustand 的 set 方法加上了额外的功能么?

这个就是中间件,和 redux 的中间件是一样的设计。
它并不需要 zustand 本身做啥支持,只要把 create 的参数设计成一个函数,这个函数接收 set、get 等函作为参数,那就自然支持了中间件。
zustand 内置了一些中间件,比如 immer、persist。
immer
1 2 3 4 5 6 7 8 9 10 11 12
| import { create } from 'zustand'; import { immer } from 'zustand/middleware/immer';
const useStore = create( immer((set) => ({ todos: [], addTodo: (text) => set((state) => { state.todos.push({ text, completed: false }); }), })) );
|
允许直接 修改对象或数组,Zustand 内部会自动进行不可变更新。
persist
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import { create } from 'zustand'; import { persist } from 'zustand/middleware';
const useStore = create( persist( (set) => ({ count: 0, increment: () => set((state) => ({ count: state.count + 1 })), }), { name: 'counter-storage', } ) );
|
作用:
- 把状态存到
localStorage
(或 sessionStorage
)。
- 刷新页面后,状态不会丢失。
stateCreator
在 Zustand 中,StateCreator
是用来定义 store 结构的一个函数类型。我们可以先使用 StateCreator
创建一个 stateCreator
,然后再将其传递给 persist
进行状态持久化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| import { create, StateCreator } from 'zustand'; import { persist } from 'zustand/middleware';
interface CounterState { count: number; increment: () => void; }
const counterStateCreator: StateCreator<CounterState> = (set) => ({ count: 0, increment: () => set((state) => ({ count: state.count + 1 })), });
const useCounterStore = create( persist(counterStateCreator, { name: 'counter-storage', getStorage: () => localStorage, }) );
function Counter() { const { count, increment } = useCounterStore();
return ( <div> <h1>{count}</h1> <button onClick={increment}>增加</button> </div> ); }
export default Counter;
|
例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| import { useEffect } from "react"; import { create } from "zustand";
const useXxxStore = create((set) => ({ aaa: "", bbb: "", updateAaa: (value) => set(() => ({ aaa: value })), updateBbb: (value) => set(() => ({ bbb: value })), }));
export default function App() { const updateAaa = useXxxStore((state) => state.updateAaa); const aaa = useXxxStore((state) => state.aaa); useEffect(() => { useXxxStore.subscribe((state) => { console.log(useXxxStore.getState()); }); }, []); return ( <div> <input onChange={(e) => updateAaa(e.currentTarget.value)} value={aaa} /> <Bbb></Bbb> </div> ); }
function Bbb() { return ( <div> <Ccc></Ccc> </div> ); }
function Ccc() { const aaa = useXxxStore((state) => state.aaa); return <p>hello, {aaa}</p>; }
|
额外方法
1 2 3 4 5 6 7 8 9 10 11 12 13
| console.log(useStore.getState().count);
useStore.setState({ count: 100 });
const unsubscribe = useStore.subscribe((state) => { console.log("新的 count:", state.count); });
unsubscribe();
|
Jotai
Jotai 是一个 react 的状态管理库,主打原子化。
在 jotai 里,每个状态都是独立的原子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| import { atom, useAtom } from 'jotai';
const aaaAtom = atom (0);
const bbbAtom = atom(0);
function Aaa() { const [aaa, setAaa]= useAtom(aaaAtom); console.log('Aaa render...') return <div> aaa: {aaa} <button onClick={() => setAaa(aaa + 1)}>加一</button> </div> }
function Bbb() { const [bbb, setBbb]= useAtom(bbbAtom);
console.log('Bbb render...')
return <div> bbb: {bbb} <button onClick={() => setBbb(bbb + 1)}>加一</button> </div> }
export default function App() { return <div> <Aaa></Aaa> <Bbb></Bbb> </div> }
|

派生atom
状态可以组合,产生派生状态:


可写atom
如果你需要创建一个 计算 & 可修改 的 atom
,可以这样:
1 2 3 4 5 6 7
| const countAtom = atom(0);
const doubleCountAtom = atom( (get) => get(countAtom) * 2, (get, set, newValue) => set(countAtom, newValue / 2) );
|
📌 解释
- 读取时:
doubleCountAtom
等于 countAtom * 2
- 写入时:如果
set(doubleCountAtom, 10)
,那么 countAtom
变成 5
在组件中使用:
1 2 3 4 5 6 7 8 9
| function DoubleCounter() { const [doubleCount, setDoubleCount] = useAtom(doubleCountAtom); return ( <div> <h2>Double Count: {doubleCount}</h2> <button onClick={() => setDoubleCount(20)}>设置为 20</button> </div> ); }
|
✅ 这类似于 Vue 里的 computed
,既能读取,也能修改!
异步atom
Jotai 可以直接处理异步数据:
1 2 3 4
| const userAtom = atom(async () => { const response = await fetch("https://jsonplaceholder.typicode.com/users/1"); return await response.json(); });
|
📌 解释
userAtom
是一个 异步 atom
- 它会自动请求数据,并在完成后更新状态
在组件中使用:
1 2 3 4
| function UserInfo() { const [user] = useAtom(userAtom); return <h2>User: {user?.name}</h2>; }
|
✅ 自动处理异步请求,类似于 SWR / React Query!
持久化atom
Jotai 提供了 atomWithStorage
,可以让 atom
**自动存到 localStorage
**:
1 2 3 4
| import { atomWithStorage } from 'jotai/utils';
const themeAtom = atomWithStorage("theme", "light");
|
✅ 作用
- 默认值是
"light"
- 状态存储在
localStorage
里(key = "theme"
)
- 刷新页面不会丢失状态!
在组件中使用:
1 2 3 4 5 6 7 8
| function ThemeToggle() { const [theme, setTheme] = useAtom(themeAtom); return ( <button onClick={() => setTheme(theme === "light" ? "dark" : "light")}> 主题:{theme} </button> ); }
|
✅ 超级简单的持久化方案,不用 persist
额外配置!