react+Zustand来管理公共数据,类似vue的pinia
前言:
在react中,我们常用的状态管理工具就是redux,像他的官方提供的工具redux Toolkit 已经很简洁,好用了,除了这个之外的话,还有出现的Zustand也能简洁高效的管理我们的公共数据,相比较redux的官方工具更简洁,类似我们vue3出来的pinia的工具,定位是一样的,这里来说一说他。
Zustand 与 Redux(及 Redux Toolkit)对比
维度 | Zustand | Redux |
---|---|---|
定位 | 轻量级状态管理(单 store) | 企业级状态管理(支持复杂场景) |
学习曲线 | 极简 API,上手快 | 概念较多(action/reducer/middleware) |
不可变性 | 默认可变(类似 Vuex) | 严格不可变(需返回新状态) |
模板代码 | 几乎为零 | Redux 较多,Redux Toolkit 减少 |
特性 | Zustand | Redux |
---|---|---|
Store 数量 | 多个独立 store | 单一 store,模块化通过 slices |
更新机制 | 直接调用 set 方法 | 必须 dispatch(action) |
性能优化 | 自动选择组件所需状态 | 需手动优化(如 shallowEqual) |
中间件 | 支持(如持久化、日志) | 生态强大(redux-thunk/saga 等) |
DevTools | 需手动集成 | 默认支持 |
TypeScript 支持 | 极佳 | 优秀(Redux Toolkit 更完善) |
具体使用:
1、安装
npm i zustand
2、简洁使用
store/index.js
import create from 'zustand';export const useCounterStore = create((set) => ({//定义变量count: 0,//定义方法,直接修改变量内容increment: () => set((state) => ({ count: state.count + 1 })), reset: () => set({ count: 0 }),}));
界面jsx中
import { useCounterStore } from '@/store'//定义好的变量和事件,直接就能取到
const { count, increment } = useCounterStore(); //用的时候,直接{}就能拿到,跟普通变量一样,事件也是直接就能调用
return <button onClick={increment}>{count}</button>;
3、项目正常使用,使用中级件
immer
和persist是 Zustand常用的中间件
immer
直接修改状态(类似 Vue 或 MobX 的写法),而 不需要手动返回新状态。(你的状态结构较复杂(如嵌套对象、数组)时,避免手写
...spread
或Object.assign
)
persist:
持久化到本地存储,刷新后丢失
immer
的具体使用方法
可以看到,immer方法里面也可以直接写变量和事件,同步异步都一样
入参是set和get,set修改变量,get获取变量内容和调用其它定义的方法
mport { create } from 'zustand';
import { immer } from 'zustand/middleware/immer';// 使用 immer 中间件
const useTodoStore = create(immer((set) => ({ //set可以简写不要括号// 定义变量todos: [],// 定义方法addTodo: (text) =>set((state) => {state.todos.push({ text, done: false });}),}))
);
persist 的具体使用方法
1、persist有两个参数,第一个是回调函数,入参两个,set可以修改定义的变量,get可以获取调用当前定义好的变量与事件,第二个是缓存的配置
2、定义变量和方法直接在里面写就行了,不用initialState、reducers这种定义方法
3、异步方法和同步方法的区别直接是可以用async来定义异步,await来掉接口,同步就是方法里写逻辑,其它没区别
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
import { loginApi } from '@/api/test'//封装的axios请求方法// 使用 persist 中间件
const useAuthStore = create(persist((set,get) => ({// 定义的变量user: null,token:'',// 定义的事件 login: (userData) => set({ user: userData }),//set方法直接修改变量内容,不用stategetToken: async () => {get().login() //可以通过这种方式,调用其它方法// 异步调用接口const res = await loginApi(form)set((state) => {state.token = res.tokenstate.user = res})},}),{name: 'auth-storage', // localStorage 的 keygetStorage: () => localStorage, // 默认是 localStorage// 可选:只持久化部分字段partialize: (state) => ({ user: state.user,token: state.token }),})
);
4、源码分享
当 immer和 persist 同时使用,immer放外面,persist放里面,参数还是 set和get
import { create } from 'zustand'
import { immer } from 'zustand/middleware/immer'
import { persist } from 'zustand/middleware'
import { useTabbarStore } from './tabbar'
import { useMenuStore } from './menu'
import { STORAGE_PREFIX, USER } from '@/config/cache'
import { loginApi, logoutApi } from '@/api/test'interface IState {token: stringuserInfo: any
}interface IActions {init: () => voidlogin: (form: any) => Promise<void>logout: () => Promise<void>
}export const useUserStore = create<IState & IActions>()(immer(persist((set, get) => ({token: '',userInfo: {},init: () => {useMenuStore.getState().init()useTabbarStore.getState().init()set((state) => {state.token = ''state.userInfo = {}})},login: async (form) => {get().init()const res = await loginApi(form)set((state) => {state.token = res.tokenstate.userInfo = res})},logout: async () => {await logoutApi().then(() => {get().init()})},}), {name: `${STORAGE_PREFIX}${USER}`,partialize: state => ({ token: state.token, userInfo: state.userInfo }),}),),
)