还在用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;
相关推荐
差点GDP4 小时前
模拟请求测试 Fake Rest API Test
前端·网络·json
酒尘&4 小时前
Hook学习-上篇
前端·学习·react.js·前端框架·react
houyhea5 小时前
从香港竹脚手架到前端脚手架:那些"借来"的发展智慧与安全警示
前端
哈哈~haha5 小时前
Step 14: Custom CSS and Theme Colors 自定义CSS类
前端·css·ui5
Ndmzi5 小时前
Matlab编程技巧:自定义Simulink菜单(理解补充)
前端·javascript·python
我命由我123455 小时前
VSCode - VSCode 修改文件树缩进
前端·ide·vscode·前端框架·编辑器·html·js
SoaringHeart6 小时前
Flutter组件封装:验证码倒计时按钮 TimerButton
前端·flutter
San30.6 小时前
深入理解 JavaScript OOP:从一个「就地编辑组件」看清封装、状态与原型链
开发语言·前端·javascript·ecmascript
AAA阿giao6 小时前
JavaScript 原型与原型链:从零到精通的深度解析
前端·javascript·原型·原型模式·prototype·原型链
烛阴7 小时前
C#异常概念与try-catch入门
前端·c#