还在用redux?试试更方便的zustand

前言

react-redux的reducer,dispatch,mapStateToProps,mapDispatchToProps等概念极大的提高了react的状态管理库的入门门槛。就是使用异步,也得借助redux-thunk。

即使后来官方推荐使用@reduxjs/toolkit,虽然一定程度上降低了心智成本,但一个仓库切片要分别导出actions和reducer,以及使用extraReducers处理异步,也需要记忆大量的API。

zustand则很好的解决了上述API复杂的问题,zustand使用create创建仓库切片,使用set改变数据状态。只要记住这两个API便能解决react的状态管理大部分情况。

zustand使用方式较为接近vue3的状态管理库pinia。想了解pinia的可参考我另一篇文章 pinia(2.0.34) 最新使用指南与持久化缓存

一、特点

  1. 完整的 ts 支持

  2. 相比于redux,@reduxjs/toolkit等库,API简单,易于上手

  3. 基于发布订阅模式实现的响应式

二、安装

yarn add zustand yarn add immer

zustand:状态管理库

immer:以更方便的方式处理不可变状态

三、使用

1. 创建 store

新建store/user.ts

js 复制代码
import { produce } from 'immer';
import { create } from 'zustand';

interface UserInfo {
  name: string;
  age: number;
}

interface UserState {
  userInfo: UserInfo;
  token: string;
  updateUserInfo: (parmas: UserInfo) => void;
  updateAge: (params: number) => void;
  updateToken: (params: string) => void;
}

// 创建状态存储
const useUserStore = create<UserState>((set) => ({
  userInfo: {
    name: 'zhangsan',
    age: 23,
  },
  token: 'S1',
  //更新整个对象
  updateUserInfo: (userInfo) => set({ userInfo }), //合并userInfo
  //更新对象中某个属性
  updateAge: (age) =>
    set(
      produce((state) => {
        state.userInfo.age = age;
      }),
    ),
  //更新原始数据类型
  updateToken: (token) => set({ token }),
}));

export default useUserStore;

2. 使用 store

新建pages/info.tsx

js 复制代码
import { useCallback } from 'react';

import useUserStore from '@/store/user';

const Info = () => {
  const { userInfo, token, updateUserInfo, updateAge, updateToken } = useUserStore();

  const hanlderUser = useCallback(() => {
    updateUserInfo({ name: 'lisi', age: 24 });
  }, [updateUserInfo]);

  const handlerAge = useCallback(() => {
    updateAge(userInfo.age + 1);
  }, [updateAge, userInfo.age]);

  const handlerToken = useCallback(() => {
    updateToken('23652');
  }, [updateToken]);

  return (
    <div className="App">
      <div>
        姓名:{userInfo.name} 年龄:{userInfo.age}
      </div>
      <div>token:{token}</div>
      <button onClick={hanlderUser}>更新用户</button>
      <button onClick={handlerAge}>更新年龄</button>
      <button onClick={handlerToken}>更新token</button>
    </div>
  );
};

export default Info;

3. 异步 action

React-redux的异步需要借助redux-thunk中间件,@reduxjs/toolkit也需借助extraReducers选项去处理

zustand可直接通过async,await来处理异步action

新建store/list.ts

js 复制代码
import { produce } from 'immer';
import { create } from 'zustand';

const getData = () => {
  return new Promise<number>((resolve) => {
    setTimeout(() => {
      resolve(Math.random() * 100);
    }, 200);
  });
};

interface ListState {
  list: number[];
  updateList: () => void;
}

const useListStore = create<ListState>((set) => ({
  list: [],
  updateList: async () => {
    try {
      const data = await getData();
      set(
        produce((state) => {
          state.list.push(data);
        }),
      );
    } catch {
      /* empty */
    }
  },
}));

export default useListStore;

四、数据持久化

1. 启用持久化

新建store/token.ts

js 复制代码
import { create } from 'zustand';
import { persist } from 'zustand/middleware';

interface TokenState {
  token: string;
  updateToken: (params: string) => void;
}

const useTokenStore = create<TokenState>()(
  persist(
    (set) => ({
      token: '',
      updateToken: (token) => set({ token }),
    }),
    {
      name: 'token', //存储的名称
    },
  ),
);

export default useTokenStore;

2. 修改存储位置

默认存储到localStorage,可以存储到sessionStorage

js 复制代码
import { create } from 'zustand';
import { persist, createJSONStorage } from 'zustand/middleware';

interface TokenState {
  token: string;
  updateToken: (params: string) => void;
}

const useTokenStore = create<TokenState>()(
  persist(
    (set) => ({
      token: '',
      updateToken: (token) => set({ token }),
    }),
    {
      name: 'token', //存储的名称
      storage: createJSONStorage(() => sessionStorage), //存储到sessionStorage
    },
  ),
);

export default useTokenStore;

3. 自定义要持久化的字段

默认会将store中的所有字段都缓存,可以通过partialize指定要缓存的字段

js 复制代码
import { produce } from 'immer';
import { create } from 'zustand';
import { persist, createJSONStorage } from 'zustand/middleware';
interface TokenState {
  user: {
    name: string;
    token: string;
  };
  updateToken: (params: string) => void;
}

const useTokenStore = create<TokenState>()(
  persist(
    (set) => ({
      user: {
        name: '',
        token: '',
      },
      updateToken: (token) =>
        set(
          produce((state) => {
            state.user.token = token;
          }),
        ),
    }),
    {
      name: 'token', //存储的名称
      storage: createJSONStorage(() => sessionStorage), //存储到sessionStorage
      partialize: (state) => ({ token: state.user.token }), //值存储token字段,而非user
    },
  ),
);

export default useTokenStore;
相关推荐
李子琪。11 分钟前
网络空间安全深度实战:CSRF 漏洞原理剖析与基于 Token 的纵深防御体系构建(全栈实验报告)
前端·安全·csrf
冰暮流星19 分钟前
javascript之history对象介绍
前端·笔记
IT_陈寒35 分钟前
Vite热更新失灵?你可能漏了这个配置
前端·人工智能·后端
丷丩36 分钟前
MapLibre GL JS第19课:实时更新要素
前端·javascript·gis·map·mapbox·maplibre gl js
Mr.Daozhi39 分钟前
RAG 进阶实战:跑通 Demo 后我连续翻了 6 次车,逐一修复才真正可用(含 Gradio Web 版)
前端·数据库·langchain·大模型·gradio·rag·科研工具
哆来A梦没有口袋1 小时前
干货精讲 | 初级CSS面试高频考题
前端·css·面试
掘金011 小时前
EmbedPDF Vue 版 完整正文文档 全网首发
前端
OpenTiny社区1 小时前
操作ArkTS页面跳转及路由相关心得
前端·typescript·web·opentiny
xiaohua0708day2 小时前
Lodash库
前端·javascript·vue.js
huakoh2 小时前
Claude Code 从零到上手指南:国产工具链复现80% Agent能力,DeepSeek+LangChain实战
前端