还在用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;
相关推荐
烂蜻蜓2 分钟前
Uniapp 中布局魔法:display 属性
前端·javascript·css·vue.js·uni-app·html
java1234_小锋31 分钟前
一周学会Flask3 Python Web开发-redirect重定向
前端·python·flask·flask3
琑951 小时前
nextjs项目搭建——头部导航
开发语言·前端·javascript
light多学一点1 小时前
视频的分片上传
前端
Gazer_S1 小时前
【Windows系统node_modules删除失败(EPERM)问题解析与应对方案】
前端·javascript·windows
bigyoung2 小时前
基于 React 的列表实现方案,包含创建和编辑状态,使用 Modal 弹框和表单的最佳实践
前端
乌木前端2 小时前
包管理工具lock文件的作用
前端·javascript
司玄2 小时前
3dtiles平移旋转原理及可视化工具实现
前端
m0_748236582 小时前
本地部署轻量级web开发框架Flask并实现无公网ip远程访问开发界面
前端·tcp/ip·flask
Jet_closer_burning2 小时前
Vue2 和 Vue3 的响应式原理对比
前端·javascript·vue.js·html