当前位置: 首页 > backend >正文

[React]实现一个类zustand公共状态库

前言

最新在’深入’学习react reRender机制时发现除了基本的setState触发reRender(props本质上也是setState后的reRender),还有第三方库比如各大百花齐放神魔乱舞的公共状态库也能通知到react去reRender对应的ui。
于是在好奇心的驱使下去查看现代的公共状态库是怎么能不触发setState还可以通知react去更新页面,最终我发现了这个被我们大多数人忽略的hooks: useSyncExternalStore

通过阅读文档可以知道

useSyncExternalStore 是一个让你订阅外部 store 的 React Hook。

具体api使用方法之类的可以自行去看文档,简单来说就是:第三方库变化的时候能通知react告知它该更新页面了。
之前的redux之类的库实现应该是用的context,通过setState来触发reRender从而达到更新ui的目的,所以这类库都需要通过Provider来通知组件更新,而react 18新增的useSyncExternalStore 则可以通过更优雅的方式来达到这个效果,所以这也是react18以来这么多公共状态库出来的原因吧,百花齐放的时代。

能收获什么?

阅读本篇并不能让你手搓一个类似zustand这样的库出来,只是以最简洁的方式展现zustand的基础功能的实现。

开始

声明一个createStore函数,这个函数入参是initFn。同zustand的create形参,它是一个函数,接收一个set,
这个set用于更新createStore的state数据。对照zustand create createStore返回的是一个useStore对象,所以他们的类型应该是:

// 定义状态更新函数类型
type SetState<T> = (upater: Partial<T> | ((prev: T) => Partial<T>)) => void;
// createStreo形参类型:接收set 返回初始状态
type CreateStore<T> = (set: SetState<T>) => T;
// createStore返回的hooks类型
type UseStore<T> = <U>(selector: (state: T) => U) => U;

初始化initFn获取创建store的数据,创建一个Set数据用于触发set时通知react;

export function createStore<T>(initFn: CreateStore<T>): UseStore<T> {let state: T;const listeners = new Set<() => void>();const set: SetState<T> = (updater) => {const nextState = typeof updater === 'function' ? updater(state) : updater;state = {...state, ...nextState};listeners.forEach(l => l());}state = initFn(set);const useStore: UseStore<T> = (selector) => {const getSnapshot = () => selector(state);const subscribe = (callback: () => void) => {listeners.add(callback);return () => listeners.delete(callback);};return useSyncExternalStore(subscribe, getSnapshot);}return useStore;
}

以上就实现了zustand的基本功能,如何使用呢?
按照规范肯定是新建一个store用于保存公共状态了,新建一个useCounterStore来替代vite模板里的count setCount

import { createStore } from '../libs/createStore';export const useCounterStore = createStore<{count: number;inc: () => void;
}>((set) => {return {count: 0,inc: () => set((s) => ({ count: s.count + 1 }))}
})

ps:这里有没有大佬告诉我怎么来写ts让它自然推到类型而不是我手动写泛型的类型啊。

在App页面引入这个状态

import { useCounterStore } from './store'
...function App() {const count = useCounterStore((s) => s.count);const setCount = useCounterStore(s => s.inc)
}...<button onClick={() => setCount()}>count is {count}</button>

此时触发这个button click点击事件就能发现count能被正确更新了,还可以自己新建几个路由页面然后使用count状态,在切换路由时自行观察。

http://www.xdnf.cn/news/9372.html

相关文章:

  • 2025上半年软考系统架构设计师选择题试题与答案
  • AI Agents执行流程和决策流程学习
  • 零基础设计模式——结构型模式 - 组合模式
  • RapidOCR4j项目学习
  • 润和星闪WS63E的MQTT示例程序存在的潜在问题
  • 经典查找算法合集(下)
  • 行为型:命令模式
  • 多语言实现插值查找算法
  • 理解vue-cli中的webpack
  • Minktec 柔性弯曲传感器,灵敏捕捉坐姿弓背、精准监测行走姿态,守护儿童背部健康,为科学健身提供数据支撑,开启职业健康与背痛 AI 干预新方向。
  • vue + ant-design + xlsx 实现Excel多Sheet页导出功能
  • 如何通过ETL对WebService进行调用
  • 顶会新方向:卡尔曼滤波+目标检测
  • Java 程序求圆弧段的面积(Program to find area of a Circular Segment)
  • Mico 1.33.1 | 解锁高级版 上千种自定义组件 动态壁纸
  • Java String函数的使用
  • 016搜索之广度优先BFS——算法备赛
  • word中表格拉不动以及插入图片有间距
  • MySQL的参数 innodb_force_recovery 详解
  • vue3+element-plus el-date-picker日期、年份筛选设置本周、本月、近3年等快捷筛选
  • JavaEE初阶-网络编程
  • 使用Mathematica绘制随机多项式的根
  • OpenCV---findCountours
  • [java八股文][JavaSpring面试篇]SpringBoot
  • 前端Vue3列表滑动无限加载实现
  • 佰力博科技与您谈谈高温介电温谱仪如何保养
  • ROS2学习(15)------ROS2 TF2 机器人坐标系管理器
  • MySQL问题:MySQL中使用索引一定有效吗?如何排查索引效果
  • LeetCode-栈-最小栈
  • 现代 CSS 高阶技巧:实现平滑内凹圆角的工程化实践