一个船新的状态管理库,开启React开发新姿势。采用了发布订阅的设计模式,同时提供了大量React hooks,以一种新
什么是状态管理库
状态管理库就是提供一种机制来集中管理和维护 React 应用中的状态,并且使得这些状态能够跨组件共享。
在2024年,已经有很多优秀的轮子可以使用,比如Redux
,mobx
,zustand
等。
今天我来推荐的是一个鄙人自己写的一个船新的状态管理库:@extremelyjs/store
github地址:github.com/extremelyjs…
采用了发布订阅的设计模式,同时提供了大量React hooks,以一种新思路来管理状态,不再需要维护type和reducer,可以实现类似useState,setState式的使用方式。
同时具备了异步更新能力和持久化存储能力,同时采用ts开发,具备了完善的类型提示。
优势
- 相比其他状态管理库,采用了类useState,setState的思想,更贴近React的开发。同时天然支持了良好的异步更新能力,持久化存储等能力。
- 写法上更加自由,心智负担更小,同时可以结合异步请求,异步事件,loading订阅和值订阅。来进一步优化代码结构,建立起统一的代码规范。
- 还支持了多种更新策略。
下载
npm i @extremelyjs/store
或者使用一个我提供的脚手架
npx @extremelyjs/cli
选择好配置之后快速创建一个项目
开始
环境要求
- React Hooks版本
- 如果需要本地持久化存储,需要在有localStorage的环境下使用(例如React native可以使用这个库,但是无法使用本地持久化能力)
订阅和更新数据
比如我们需要建立一个store,后续有一个更新组件可以使用他的更新能力,另一个组件可以使用他的订阅数据的能力。就可以建立一个如下的store.
// 创建num.ts这个store
import { createMapperHooksStore } from '@extremelyjs/store'
const numStore = createMapperHooksStore<number>(0)
export const useNum = numStore.useStoreValue // 监听state变化
export const setNum = numStore.setStoreValue // 修改state,支持value或者callback
export const resetNum = numStore.reset // 重置state
如过我们想订阅这个数据,就可以使用导出的use函数
const num = useNum();
直接在一个组件里面使用,就可以订阅后续所有的变化。
在更新的时候,我们也不必像其他的store库一样,关注和维护很多type,action等内容。而是可以直接像在React中使用useState,setState一样去set改变。就像React中的set会支持value或者回调,在该状态管理库中同样也支持传入value或者回调来使用。
例如我们可以在改变事件的组件中这样使用:
// 传入回调,在之前的基础上+1。
const handleClick = useCallback(
() => {
setNum(value => value + 1);
},[]
);
// 传入value,直接改变值。
const handleChangeValue = useCallback(
() => {
setNum(10);
},[]
);
或者在一些复杂的场景,我们可能喜欢封装好逻辑让其他开发者调用,也可以在内部封装好再导出,栗子如下:
import { createMapperHooksStore } from '@extremelyjs/store'
const numStore = createMapperHooksStore<number>(0)
export const useNum = numStore.useStoreValue // 监听state变化
export const setNum = numStore.setStoreValue // 修改state,支持value或者callback
// 封装一个简单的自增逻辑,正常这种不复杂的逻辑应该写到外面的,但是为了演示思想,那么这里就封装一个。
export const increment = () => setNum(n => n + 1);
export const resetNum = numStore.reset // 重置state
持久化
对于支持localStorage的环境,可以使用持久化能力。
我们传入第二对象参数withLocalStorage,并设置好key,即可使用持久化能力。
const num = createMapperHooksStore<number>(0, {withLocalStorage: 'keyName'}) // keyName为自定义id
异步更新能力
请求订阅
对于异步更新,可以使用异步更新能力。比如一个纯粹的api的请求,我们已经明确了他的输入输出,便可以放入loadStoreValue
来自动更新数据,或者订阅loading过程,无需额外的心智负担,同时也方便我们建立统一而优雅的开发规范。同时我们的ts也会在开发阶段做一些校验,对于参数/返回值与store中的值不符的情况,也可以及时发现并修改。
import { createMapperHooksStore } from "@extremelyjs/store";
// fetchCurrentPageContent是一个输出结果为string,入参为PageDataParams的一段fetch请求。
import fetchCurrentPageContent from "../api/fetchCurrentPageContent";
import { PageDataParams } from "../type/params";
const pageDataStore = createMapperHooksStore<string,PageDataParams>('', { withLocalStorage: 'page-data-new' });
export const usePageData = pageDataStore.useStoreValue; // 订阅state变化
export const usePageDataLoading = pageDataStore.useStoreLoading; // 订阅Loding状态
// 异步更新,支持传入参数
export const loadPageData = pageDataStore.loadStoreValue(
params => params,
fetchCurrentPageContent
);
异步事件的订阅
我们还提供了load
的api来订阅异步任务。
function mockPromiseArray() {
// 模拟十个异步任务,返回数字
const arr: Promise<string>[] = []
for (let i = 0; i < 10; i++) {
arr.push(new Promise((resolve) => {
setTimeout(() => resolve(String(i)), 1000 * i);
}))
}
return arr;
}
// store文件
import { createMapperHooksStore } from "@extremelyjs/store/src/index";
const testAsyncStore = createMapperHooksStore<string>("",{strategy: "acceptSequenced"});
export const useTestAsync = testAsyncStore.useStoreValue;
export const loadTestAsync = testAsyncStore.load;
// react组件
const testAsync = useTestAsync();
同时我们还支持四种异步更新策略
你可以使用 strategy
配置异步策略,目前提供了四种异步策略:
策略 | 描述 |
---|---|
acceptFirst | 在多个异步任务同时发出的情况下,只接受第一个成功的结果。如果已经有成功的返回,则后续请求不再发出。 |
acceptLatest | 在多个异步任务同时发出的情况下,只接受最后一个发出的任务的结果,成功或失败。 |
acceptEvery | 在多个异步任务同时发出的情况下,接受所有的返回,按照到达的顺序处理。由于到达的顺序可能是乱序,你需要处理乱序导致的问题。 |
acceptSequenced | 在多个异步任务同时发出的情况下,按照任务发出的顺序,接受结果,当中间的任务到达时,则不再接受此任务之前发起的任务的结果,但依旧等待后续发出的结果。 |
默认使用 acceptSequenced
的策略,这个策略满足绝大多数情况,在你需要特别的优化的时候,你可以选择其他的策略。
直接取值
我们还提供了getStoreValue
和getStoreLoading
来直接取值。
Todo
- 完善文档
- 对于ReactNaitve,小程序的本地持久化支持
- 更好的错误提示
- 测试用例的完善
- 异步策略的完善
转载自:https://juejin.cn/post/7408816726027337768