2021年star飙升最快的react状态管理工具zustand

548 阅读2分钟

如何使用zustand替换@reduxjs/toolkit

背景

最近公司有个h5的需求,h5相对简单,移动端嘛,讲究的是轻巧。
之前pc端用的状态管理是Redux Toolkit 确实很强大好用,但是对于h5来说体积太大了。

看下npm上的包体积对比

2.png

1.png

基本的使用

创建 store/index.ts

// store/index.ts
import create from "zustand";

type HandleType = "add" | "jianshao";

interface InitialState {
  count: number;
  userInfo: { name?: String; age?: number };
  changeCount: (type: HandleType) => void;
  fetchData: (type?: any) => void;
}

const useGLobalStore = create<InitialState>((set, get) => ({
  count: 0,
  userInfo: {},
  changeCount: (type) => {
    if (type === "add") {
      // 对象写法通过get可以获取到store中的数据
      set({ count: get().count + 1 });
    } else {
      // 也可以通过函数写法获取到store中的数据
      set((state) => ({ count: state.count - 1 }));
    }
  },
  fetchData: async (params?: any) => {
    const res: any = await requestData(params);
    set({ userInfo: res.data.userInfo });
  },
}));

export { useGLobalStore };

// 模拟异步请求
function requestData(params?: any) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve({
        data: {
          userInfo: {
            name: "jianjian",
            age: 12,
          },
        },
      });
    }, 2000);
  });
}

使用

import { useGLobalStore } from "./store";
import shallow from "zustand/shallow";

const App = () => {
  const { count, userInfo, changeCount, fetchData } = useGLobalStore(
    (state) => ({
      count: state.count,
      userInfo: state.userInfo,
      changeCount: state.changeCount,
      fetchData: state.fetchData,
    })
  );
  
  // 也可以这样获取
  // const count = useGLobalStore().count;
  // const changeCount = useGLobalStore().changeCount;
  // const fetchData = useGLobalStore().fetchData;
  // const userInfo = useGLobalStore().userInfo;
  
  return (
    <div>
      <span>count: {count}</span>
      <span>name: {userInfo.name}</span>
      <span>age: {userInfo.age}</span>
      <button onClick={() => changeCount("add")}>增加</button>
      <button onClick={() => changeCount("jianshao")}>减少</button>
      <button onClick={() => fetchData()}>请求数据</button>
    </div>
  );
};

export default App;

结合中间件的使用

修改store/index.ts

import create from "zustand";
++import { devtools, persist } from "zustand/middleware";

type HandleType = "add" | "jianshao";

interface InitialState {
  count: number;
  userInfo: { name?: String; age?: number };
  changeCount: (type: HandleType) => void;
  fetchData: (type: any) => void;
}

++为啥create<InitialState>()这里要加括号 可[查看](https://github.com/pmndrs/zustand/blob/main/docs/guides/typescript.md)
++const useGLobalStore = create<InitialState>()(
++persist(
++    devtools((set, get, api) => ({
++      count: 0,
++      userInfo: {},
++      changeCount: (type) => {
++        if (type === "add") {
++          // 对象写法通过get可以获取到store中的数据
++          set({ count: get().count + 1 });
++       } else {
++         // 也可以通过函数写法获取到store中的数据
++         set((state) => ({ count: state.count - 1 }), false, "global/change");
++        }
++      },
++      fetchData: async (params: any) => {
++        const res: any = await requestData(params);
++        set((state) => ({ userInfo: res.data.userInfo }), false);
++        // set((state) => ({ userInfo: res.data.userInfo }), true);
++        // set({ userInfo: res.data.userInfo });
++      },
++    })),
++    { name: "jianjian" }
++  )
++);

export { useGLobalStore };

function requestData(params: any) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve({
        data: {
          userInfo: {
            name: "jianjian",
            age: 12,
          },
        },
      });
    }, 2000);
  });
}

1 我们引入了中间价devtools(可以调用redux的浏览器工具)persist(可以讲zustand中的数据存储在localstorage中)

2 set((state) => ({ count: state.count - 1 }), false, "global/change");第二个参数默认为false, 修改数据会和之前的数据进行合并, 如何为true, 会把之前的数据替换,而不是合并(慎用)第三个参数global/change就是我们在redux调试工具中的type类型

3 上面例子中的 { name: "jianjian" } 是我们调用persist中间件存在localstorage中的key

如何实现模块化管理

新建文件 store/bearStore.ts

import type { StateCreator } from "zustand";
import { FishSlice } from "./fishStore";

export interface BearSlice {
  bears: number;
  addBear: () => void;
  reduceBear: () => void;
}
export const createBearSlice: StateCreator<
  BearSlice & FishSlice,
  [["zustand/persist", unknown], ["zustand/devtools", never]],
  [],
  BearSlice
> = (set) => ({
  bears: 0,
  addBear: () => set((state) => ({ bears: state.bears + 1 })),
  reduceBear: () => set((state) => ({ bears: state.bears - 1 })),
});

新建文件 store/fishStore.ts

import type { StateCreator } from "zustand";
import { BearSlice } from "./bearStore";

export interface FishSlice {
  fishes: number;
  addFish: () => void;
   : () => void;
}

export const createFishSlice: StateCreator<
  FishSlice & BearSlice,
  [["zustand/persist", unknown], ["zustand/devtools", never]],
  [],
  FishSlice
> = (set) => ({
  fishes: 0,
  addFish: () => set((state) => ({ fishes: state.fishes + 1 })),
  eatFish1: () => set((state) => ({ fishes: state.fishes - 1 })),
});

修改store/index.ts

import create from "zustand";
import { devtools, persist } from "zustand/middleware";
import { FishSlice, createFishSlice } from "./fishStore";
import { BearSlice, createBearSlice } from "./bearStore";

const useGLobalStore = create<BearSlice & FishSlice>()(
  persist(
    devtools((...a) => {
      return {
        ...createBearSlice(...a),
        ...createFishSlice(...a),
      };
    })
  )
);
export { useGLobalStore };