@yhfu/re-gen v0.4.0
基于rxjs的状态生成器
NPM地址 @yhfu/re-gen @yhfu/re-gen-hooks
demo地址: https://stackblitz.com/edit/react-ts-wv4a9d?file=App.tsx,config.ts,index.html
🚀 此库功能基本完备,测试demo均通过,可放心使用
为什么会有该库
💡 最初的想法是想实现一个状态和UI分离的开发方式,能够开发一个工具对复杂的状态进行构建
💡 之后探索了一下 RxJS 的开发模式,发现我想做的工具其实就是使用 RxJS 开发的一个较为通用的开发模式
💡 同时也想探索一种 RxJS in React 的实现方案
什么场景适合使用?
它可以应用到平时开发中的任何场景,只是不同场景的接入成本不同。
Demo 说明
apps/FormFilter
这是一个实际的 demo,你会发现在这种场景下工具使用起来会非常的丝滑,因为这个工具的诞生就是为了这种场景的需求。它主要以多个不同的 field 之间进行联动,其次就是比较关键的地方在于它们的数据源都来自组件,包括 change click 等进行驱动,这也是 RxJS 能适用的原因之一。
app/Form
这是一个非常规的demo,主要展示的是一个数据是通过其他数据源驱动更新自身(reduce)的一个场景。因为 RxJS 自身的原因,并不能很好地区分到底是哪个依赖产生了变化,所以需要写一些功能性( 例如去重 )之类的代码,或者是引入新的变量来标识具体是哪个变量发生了变化( 类似于 redux 派发 action )。
接入方式
如果使用该工具,需要提供一组配置项,单个配置项的格式如下所示,具体使用方式可以参照 apps/demo/src/config.ts 的配置文件。
interface IConfigItem {
name: string;
init?: Promise | Observable | PlainResult | InitFunctionType;
handle?: (arg: any) => ReturnResult;
distinct?: IDistinct;
reduce?: {
handle: (pre: any, val: any) => any;
init: any;
};
filterNil?: FilterNilOption;
depend?: {
names: string[];
handle: (args: any) => ReturnResult;
combineType?: CombineType;
};
}
具体使用方法
详细代码可参考 apps/demo 项目 https://stackblitz.com/edit/react-ts-wv4a9d?file=App.tsx,config.ts,index.html
- 创建配置项列表
// 处理函数支持 async/await 以及返回 Observable 的形式
// 如果你熟悉 RxJS 的话,那将会有很好的体验
export const RelationConfig: IConfigItem[] = [
{
name: "area",
handle(val) {
return of(val);
},
},
{
name: "region",
handle: (val: string[] = []) =>
from(val).pipe(
filter(Boolean),
map((item) => item?.toLocaleUpperCase()),
toArray()
),
},
{
name: "showRegion",
init: false,
depend: {
names: ["area"],
handle([show, area]: [show: boolean, area: string]) {
if (area === "CN") {
return true;
}
return false;
},
},
},
{
name: "testMoreDepend",
init: "",
depend: {
names: ["showRegion", "RegionList"],
handle: async ([testMoreDepend, showRegion, RegionList]: [
testMoreDepend: string[],
showRegion: boolean,
RegionList: string[]
]) => JSON.stringify(showRegion) + JSON.stringify(RegionList?.length),
},
},
{
name: "testMoreMoreDepend",
init: "",
depend: {
names: ["testMoreDepend"],
handle: async ([testMoreMoreDepend, testMoreDepend]: [
testMoreMoreDepend: string,
testMoreDepend: string
]) => {
if (
testMoreDepend === "true4" && testMoreMoreDepend !== "out"
) {
return "full";
} else {
if (testMoreMoreDepend === "out") {
return "out";
}
return "unfull";
}
},
},
},
];
- 通过包导出的
ReGen
方法获取AtomInOut
方法
const AtomInOut = ReGen( CacheKey, RelationConfig );
// 该工具通过 CacheKey 进行区分存储的状态,相同的 CacheKey 会获取相同的状态。
// 建议在组件外部进行调用,避免重复渲染。但是该函数做了缓存处理,写在组件里也不会造成性能浪费。
接下来可以使用hook进行操作
hook方法
- 通过使用
@yhfu/ge-ren-hooks
包导出的useAtomsValue
以及useAtomsCallback
方法,分别传入AtomInOut
以及RelationConfig
参数,hook会返回一个对象,通过解构对象,从而获取${name}
以及${name}Callback
。其中${name}
会被替换为RelationConfig
中的name值。
const AtomInOut = ReGen( CacheKey, RelationConfig, { logger: { duration: 300 } } ); // 可以写到组件外边,也可以写到组件内部,实际通过 CacheKey 做了缓存的处理
const {
area,
region
} = useAtomsValue( AtomInOut, RelationConfig );
const {
areaCallback,
regionCallback
} = useAtomsCallback( AtomInOut, RelationConfig );
全局可选配置项
调试日志
日志服务使用的是 rxjs-watcher
的库。开启方法是传入第三项配置项 logger: { duration?:number } | boolean
,其中 duration
为可观察的持续时间。如需看到每个 Observable 的具体情况,请安装 rxjs-watcher
相关浏览器插件即可。
ReGen( CacheKey, RelationConfig, { logger: { duration: 180 } } )
空值处理
// 全局配置
// 默认值 false
// type FilterNilOption = "All" | "Default" | "In" | "HandleAfter" | "DependAfter" | "Out"
// All 表示全局开启过滤 Default 表示默认策略(全部关闭) "In" | "HandleAfter" | "DependAfter" | "Out" 表示不同的阶段进行空值处理
ReGen( CacheKey, RelationConfig, { nil: 'Default' } );
// 局部配置
// 单独对 area 进行空值
// 优先级高于全局配置
[{
name: "area",
handle( val ) {
return [val];
},
filterNil: FilterNilOption
}]
combineLatestWith
的处理方式是当所有的Observable
都有值的时候,才会通过第一个值
withLastestFrom
的处理方式是其他的Observable
有值之后,再次触发上游Observable
才会通过第一个值所以在过滤的时候如果不熟悉上边的条件,建议每个配置项都写上
init
并且不对filterNil
进行设置
重复值过滤
默认全局开启,使用 ramda
中的 equals
进行对比
ReGen( CacheKey, RelationConfig, { distinct: true } );
也可以在单独的配置项中进行设置,类型如下所示
type IDistinct<T, K> =
| boolean
| {
comparator: ( previous: K, current: K ) => boolean; keySelector?: ( value: T ) => K;
};
10 months ago
10 months ago
8 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago