说说zustand的缺点——对几个用过的状态管理工具的总结

主要是与mobX的一些对比,redux和recoil很久没用了

一家之言,我是彩笔

redux

记忆里redux的不可变树特性会导致一些意料外的刷新(应改是有办法解决的才对),并且心智负担有点重,需要自己构建action等,虽然又toolkit的加入据说会好很多,但是最后还是被我放弃了,太累了

mobX

我认为他是最简单的能够对应上OOP的一种状态管理工具了,装饰器语法很棒,我没有用得很深并且有一阵子没用了,说些自己的见解

  1. 最新版还能用装饰器语法吗?如果支持也只是 怕别用用别怕,我永远忘不了它,makeObservable 一个一个加还是麻烦,而且 construct 跟实际的代码分离,最后还不如 makeAutoObservable
  2. makeAutoObservable 或许并不是最好的选择:取决于你是否将 mobx object 单纯地作为一个 store 处理

mobx 中属性是否是 state/action 应该是有选择地去处理而不是交给 auto,整个class可以根据业务封装成独立的 data manager model,根据实际的使用情况,内挂很多private属性/纯函数等与state无关,但是与业务相关的东西,和 hook 的构建类似,应该是基于业务的OOP

zustand

最近在用,并非本人选型,但是用着还行,追求极致的打包大小时这是个好选择,项目规模大时建议不要考虑

优点
  • 不像mobx还需要observer包裹组件
  • 体积小,但是其实没差多少,不是极致追求体积的场景我觉得差别不大
说说缺点
ts 复制代码
const list = useListStore(state=>state.list);
// ...
list.map
  • jsdoc 注释以及引用关系会在这里丢失,list 只会指向 const list =,需要继续查找state.list的定义
  • 他是 hook 的用法,use 开头一个 store 很怪,如果命名不以use开头, userStore(...)这样作为方法调用一个 store 也很怪,始终没有想到一个简单明了的语义设计
ts 复制代码
// 构建方式1
interface StoreType {
  /** xxx 用途的列表 */
  list: { id: string }[];
}

const useListStore = create<StoreType>((set, get) => ({
  /** 这里注释并没有什么用处 */
  list: [],
}));
  • 类型和数据分离,跟上面一样,如果要查找初始值,查找定义时,会跳到类型,然后根据类型查找,要在一堆引用里找到 store 的初始定义
  • 当属性很多(虽然不应该让他很多),类型很长时,需要将 StoreType 分文件管理,更难受了
ts 复制代码
// 构建方式2
// 如果需要自动推断类型,需要不传入 set get
const useListStore = create(() => ({
  /** xxx 用途的列表 */
  list: [] as { id: string }[],
}));
  • 构建方式2可以避免上面的问题,但是每个初始值都需要去断言,依然会存在jsdoc丢失的问题,并且无法在 store 内直接使用 set/get 而需要 useListStore.setState 这与库的原始设计相悖,这样比起来基于 class 的 mobx 在使用上要舒服很多,可以 list:Type = []
  • 官方示例中 将 action 写在 store object 内真的是很迷的一步,为了聚合一个 state 相关的逻辑,会在 store object 内写很大的一块,我在实践中更多的是基于文件块进行管理,尽可能降低 store object 本身的行数(主要还是减少全局 state 的使用),让逻辑更加清晰
ts 复制代码
import { create } from 'zustand';

export interface BaseStore {
  /** 广告 - 打开推荐创作者 */
  excellentShown: boolean;
  /** 打开编辑精选 - 当前展示期数
   * - `''` 为默认期数
   * - `undefined` 为关闭编辑精选
   */
  editPickPeriod?: number | string;
  /** 排行榜(如果分成两个状态处理会导致 effect hook 执行两次)
   * - `undefined` 为关闭排行榜
   */
  rankingSource?: {
    type: 'fc_day' | 'fc_week' | 'active_day' | 'active_week';
    /** 排行榜期数 */
    period?: string | number;
  };
  /** 是否显示提现页 */
  payoutPageShown: boolean;
  /** feed 流 socket 触发的数量变化未加载的数量 */
  newFeedCount: number;
  /** null 表示显式的未获取到,undefined 表示 prefetch 可以再等等 */
  customLink?: string | null;
}

const baseStore = create<BaseStore>((set, get) => ({
  excellentShown: false,
  editPickPeriod: undefined,
  rankingSource: undefined,
  payoutPageShown: false,
  newFeedCount: 0,
}));

export default baseStore;

const get = baseStore.getState;
const set = baseStore.setState;

export const baseStoreUtils = {
  /** getter - 右下角固定按钮显隐 */
  getFixedToolsHidden: () => {
    const { payoutPageShown } = get();
    return !!payoutPageShown;
  },
  /** 重置设置会导致右下角固定按钮显隐变化的状态 */
  resetFixedToolsHiddenState: () => set({ payoutPageShown: false }),
  setPayoutPageShown: (shown: boolean) => set({ payoutPageShown: shown }),
};
  • 基于AOP(面向切片)的概念,无论是 action 还是 getter 都是很有必要的,前几天查一个数据变更行为导致的bug时,面对💩山里遍地的 xxxStore.setState({xxx:xxx}) 我是十万分的崩溃,zustand 在设计上非常的灵活,这在编码上是很方便,但在项目管理上真的很让人头疼,无法制造 readonly 的 getter 导致一堆对项目没有敬畏的代码出现,这不是好事

结论

无论选型什么状态管理工具,其实差别都没有那么大,至少在人与人的差距面前是这样的,遇到堆💩人,用什么都救不了,面对项目管理的工作,新的不一定是好的,coooooool 的代码需要 cool 的队友

相关推荐
语落心生6 分钟前
探秘新一代向量存储格式Lance-format (三) Lance 数据类型系统
架构
烛阴8 分钟前
从`new`关键字开始:精通C#类与对象
前端·c#
语落心生11 分钟前
探秘新一代向量存储格式Lance-format (二) 项目结构与模块划分
架构
天若有情67312 分钟前
笑喷!乌鸦哥版demo函数掀桌怒怼主函数:难办?那就别办了!
java·前端·servlet
记得记得就15112 分钟前
【Nginx 实战系列(一)—— Web 核心概念、HTTP/HTTPS协议 与 Nginx 安装】
前端·nginx·http
天蓝色的鱼鱼13 分钟前
mescroll老用户亲测z-paging:这些功能让我果断切换!
前端·uni-app
JarvanMo18 分钟前
适用于 iOS 开发者的 Flutter 3.38:Xcode 与 Swift 集成新技巧
前端
语落心生19 分钟前
探秘新一代向量存储格式Lance-format (一)Lance 项目概览与设计理念
架构
北极糊的狐30 分钟前
Vue 中 vue-awesome-swiper的使用笔记(适配 Vue2/Vue3)
前端·javascript·vue.js
anyup34 分钟前
🔥100+ 天,已全面支持鸿蒙!uView Pro 近期更新盘点及未来计划
前端·uni-app·harmonyos