前言

React的状态管理是一个缤纷繁杂的大世界,光我知道的就不下数十种,其中有最出名immutable阵营的redux,有mutable阵营的mobxreact-easy-state,在hooks诞生后还有极简主义的unstated-next,有蚂蚁金服的大佬出品的hoxhoox

其实社区诞生这么多种状态管理框架,也说明状态管理库之间都有一些让人不满足的地方。

rxv是我依据这些痛点,并且直接引入了Vue3的package: @vue/reactivity去做的一个React状态管理框架,下面先看一个简单的示例:

示例

// store.ts
import { reactive, computed, effect } from '@vue/reactivity';export const state = reactive({count: 0,
});const plusOne = computed(() => state.count + 1);effect(() => {console.log('plusOne changed: ', plusOne);
});const add = () => (state.count += 1);export const mutations = {// mutationadd,
};export const store = {state,computed: {plusOne,},
};export type Store = typeof store;
// Index.tsx
import { Provider, useStore } from 'rxv'
import { mutations, store, Store } from './store.ts'
function Count() {const countState = useStore((store: Store) => {const { state, computed } = store;const { count } = state;const { plusOne } = computed;return {count,plusOne,};});return (<Card hoverable style={{ marginBottom: 24 }}><h1>计数器</h1><div className="chunk"><div className="chunk">store中的count现在是 {countState.count}</div><div className="chunk">computed值中的plusOne现在是 {countState.plusOne.value}</div><Button onClick={mutations.add}>add</Button></div></Card>);
}export default () => {return (<Provider value={store}><Count /></Provider>);
};

可以看出,store的定义只用到了@vue/reactivity,而rxv只是在组件中做了一层桥接,连通了Vue3和React,正如它名字的含义:React x Vue。

一些痛点

根据我自己的看法,我先简单的总结一下现有的状态管理库中或多或少存在的一些不足之处:

  1. redux为代表的,语法比较冗余,样板文件比较多。
  2. mobx很好,但是也需要单独的学一套api,对于react组件的侵入性较强,装饰器语法不稳定。
  3. unstated-next是一个极简的框架,对于React Hook做了一层较浅的封装。
  4. react-easy-state引入了observe-util,这个库对于响应式的处理很接近Vue3,我想要的了。

下面展开来讲:

options-based的痛点

Vuex和dva的options-based的模式现在看来弊端多多。具体的可以看尤大在vue-composition-api文档中总结的。

简单来说就是一个组件有好几个功能点,但是这几个功能点在分散在data,methods,computed中,形成了一个杂乱无章的结构。

当你想维护一个功能,你不得不先完整的看完这个配置对象的全貌。

心惊胆战的去掉几行,改掉几行,说不定会遗留一些没用的代码,也或者隐藏在computed选项里的某个相关的函数悄悄的坑了你...

而hook带来的好处是更加灵活的代码组织方式。

redux

直接引入dan自己的吐槽吧,要学的概念太多,写一个简单的功能要在五个文件之间跳来跳去,好头疼。redux的弊端在社区被讨论也不是一天两天了,相信写过redux的你也是深有同感。

unstated-next

unstated-next其实很不错了,源码就40来行。最大程度的利用了React Hook的能力,写一个model就是写一个自定义hook。但是极简也带来了一些问题:

  1. 模块之间没有相互访问的能力。
  2. Context的性能问题,让你需要关注模块的划分。(具体可以看我这篇文章的性能章节
  3. 模块划分的问题,如果全放在一个Provider,那么更新的粒度太大,所有用了useContext的组件都会重复渲染。如果放在多个Provider里,那么就会回到第一条痛点,这些模块之间是相互独立的,没法互相访问。
  4. hook带来的一些心智负担的问题。React Hooks 你真的用对了吗?

react-easy-state

这个库引入的observe-util其实和Vue3 reactivity部分的核心实现很相似,关于原理解析也可以看我之前写的两篇文章:带你彻底搞懂Vue3的Proxy响应式原理!TypeScript从零实现基于Proxy的响应式库。带你彻底搞懂Vue3的Proxy响应式原理!基于函数劫持实现Map和Set的响应式。

  1. 似乎不维护了。
  2. 对于异步队列的处理过于hack,带来了很多issue,而且一直没有解决。

那其实转而一想,Vue3 reactivity其实是observe-util的强化版,它拥有了更多的定制能力,如果我们能把这部分直接接入到状态管理库中,岂不是完全拥有了Vue3的响应式能力。

原理分析

vue-next是Vue3的源码仓库,Vue3采用lerna做package的划分,而响应式能力@vue/reactivity被划分到了单独的一个package中

从这个包提供的几个核心api来分析:

effect

effect其实是响应式库中一个通用的概念:观察函数,就像Vue2中的Watcher,mobx中的autorunobserver一样,它的作用是收集依赖

它接受的是一个函数,这个函数内部对于响应式数据的访问都可以收集依赖,那么在响应式数据更新后,就会触发响应的更新事件。

reactive

响应式数据的核心api,这个api返回的是一个proxy,对上面所有属性的访问都会被劫持,从而在get的时候收集依赖(也就是正在运行的effect),在set的时候触发更新。

ref

对于简单数据类型比如number,我们不可能像这样去做:

let data = reactive(2)
//  oops
data = 5

这是不符合响应式的拦截规则的,没有办法能拦截到data本身的改变,只能拦截到data身上的属性的改变,所以有了ref。

const data = ref(2)
//  ok
data.value= 5

computed

计算属性,依赖值更新以后,它的值也会随之自动更新。其实computed内部也是一个effect。

拥有在computed中观察另一个computed数据、effect观察computed改变之类的高级特性。

实现

从这几个核心api来看,只要effect能接入到React系统中,那么其他的api都没什么问题,因为它们只是去收集effect的毅力,去通知effect触发更新。

effect接受的是一个函数,而且effect还支持通过传入schedule参数来自定义依赖更新的时候需要触发什么函数,

rxv的核心api: useStore接受的也是一个函数selector,它会让用户自己选择在组件中需要访问的数据。

那么思路就显而易见了:

  1. selector包装在effect中执行,去收集依赖。
  2. 指定依赖发生更新时,需要调用的函数是当前正在使用useStore的这个组件的forceUpdate强制渲染函数。

这样不就实现了数据变化,组件自动更新吗?

简单的看一下核心实现

export const useStore = <T, S>(selector: Selector<T, S>): S => {const forceUpdate = useForceUpdate();const store = useStoreContext();const effection = useEffection(() => selector(store), {scheduler: forceUpdate,lazy: true,});const value = effection();return value;
};

  1. 先通过useForceUpdate在当前组件中注册一个强制更新的函数。
  2. 通过useContext读取用户从Provider中传入的store。
  3. 再通过Vue的effect去帮我们执行selector(store),并且指定scheduler为forceUpdate,这样就完成了依赖收集。

就简单的几行代码,就实现了在React中使用@vue/reactivity中的所有能力。

优点:

  1. 直接引入@vue/reacivity,完全使用Vue3的reactivity能力,拥有computed, effect等各种能力,并且对于Set和Map也提供了响应式的能力。后续也会随着这个库的更新变得更加完善的和强大。
  2. vue-next仓库内部完整的测试用例。
  3. 完善的TypeScript类型支持。
  4. 完全复用@vue/reacivity实现超强的全局状态管理能力。
  5. 状态管理中组件级别的精确更新。
  6. Vue3总是要学的嘛,提前学习防止失业!

缺点:

  1. 由于需要精确的收集依赖全靠useStore,所以selector函数一定要精确的访问到你关心的数据。甚至如果你需要触发数组内部某个值的更新,那你在useStore中就不能只返回这个对象本身。

源码地址

https://github.com/sl1673495/react-composition-api

如果你喜欢这个库,欢迎给出你的star✨,你的支持就是我最大的动力~

react全局状态管理_rxv: 在React中用Vue3的reactivity包实现状态管理。相关推荐

  1. nodeJS web端包使用 bower 管理

    使用 npm 管理 server 端(服务端)的包 使用 bower 管理 web 端(客户端)的包 bower 的使用: 1.使用 npm 安装 bower npm install bower -g ...

  2. React 全局状态管理的 3 种底层机制

    现代前端框架都是基于组件的方式来开发页面.按照逻辑关系把页面划分为不同的组件,分别开发不同的组件,然后把它们一层层组装起来,把根组件传入 ReactDOM.render 或者 vue 的 $mount ...

  3. [react] 状态管理器解决了什么问题?什么时候用状态管理器?

    [react] 状态管理器解决了什么问题?什么时候用状态管理器? 专注 view 层 React 官网是这么简介的.JavaScript library for building user inter ...

  4. react全局方法_前端面试题 ---react

    高阶组件相关 什么是高阶组件,它有哪些运用? 高阶组件就是一个函数,接收一个组件,经过处理后返回后的新的组件: 高阶组件,不是真正意义上的组件,其实是一种模式: 可以对逻辑代码进行抽离,或者添加某个共 ...

  5. react引入多个图片_重新引入React:v16之后的每个React更新都已揭开神秘面纱。

    react引入多个图片 In this article (and accompanying book), unlike any you may have come across before, I w ...

  6. react前端开发_是的,React正在接管前端开发。 问题是为什么。

    react前端开发 by Samer Buna 通过Samer Buna 是的,React正在接管前端开发. 问题是为什么. (Yes, React is taking over front-end ...

  7. react jest测试_如何使用React测试库和Jest开始测试React应用

    react jest测试 Testing is often seen as a tedious process. It's extra code you have to write, and in s ...

  8. react leaflet_如何使用Leaflet在React中轻松构建地图应用

    react leaflet Mapping is hard, but spinning up a new app that renders maps doesn't have to be. Here' ...

  9. 创建react应用程序_如何使用React创建一个三层应用程序

    创建react应用程序 Discover Functional JavaScript was named one of the best new Functional Programming book ...

最新文章

  1. 什么是B/S模式?什么是C/S模式?
  2. 类脑信息处理研究取得进展
  3. 金额转换,阿拉伯数字的金额转换成 面试编程题
  4. valid floating point value什么意思_每个位置的球员,都在干些什么
  5. 安装ssr_12月23日先锋首测启动,累计登录3天必得SSR!
  6. icpc网络赛第二场K Meal
  7. 虚拟仿真引擎消息机制
  8. [导入]给老家新楼的对联
  9. 命令行下(bat)使用 Lhaplus 自动 压缩 解压文件 (含参数设定说明)
  10. 让Google earth叠加中文地图
  11. linux date英文日期、星期简写说明(中文对照)
  12. linux下优盘格式化,linux下格式化u盘
  13. 09_Mybatis-plus类型处理器示例,例如 json 字段对象转换
  14. 2022年ABC123公众号年刊下载(PDF电子书)
  15. 高项.十大管理47个过程
  16. Java-实现购物车功能小系统
  17. 二进制数字调制器的设计
  18. 面试京东测试岗,总结了这些笔试细节!
  19. SAP 接口 inbound (SAP CALL JAVA ) 负载均衡说明
  20. 微信小程序向java后端发送对象时 ,后端接收不到

热门文章

  1. 6月Unity技术路演华东站报名启动!
  2. 在ASP.NET Core中使用Apworks开发数据服务:对HAL的支持
  3. android防止左向右滑出程序,Android——ViewPager禁止左右滑动的实现
  4. 【ArcGIS风暴】在ArcGIS中实现将一个圆16等分
  5. Android Studio之提示Gradle sync failed: Plugin with id ‘com.novoda.bintray-release‘ not found.
  6. LeetCode之Find the Difference
  7. Android之应用坐标系统全面详解
  8. Android之AsyncTask异步任务详解总结
  9. php基础教程 第七步数组补充及循环基础
  10. java gui 按键 数组_java GUI分配数组值