0.4.0 • Published 8 months ago

@yhfu/re-gen v0.4.0

Weekly downloads
-
License
-
Repository
github
Last release
8 months ago

基于rxjs的状态生成器

img.png

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;
};
0.3.2-alpha

10 months ago

0.3.5

10 months ago

0.4.0

8 months ago

0.3.2-beta

10 months ago

0.3.1

10 months ago

0.3.4

10 months ago

0.3.3

10 months ago

0.2.4

10 months ago

0.2.1

1 year ago

0.2.0

1 year ago

0.1.5-alpha

1 year ago

0.1.8

1 year ago

0.1.7

1 year ago

0.1.9

1 year ago

0.2.3

1 year ago

0.1.4

1 year ago

0.2.2

1 year ago

0.1.6

1 year ago

0.1.5

1 year ago

0.1.3

1 year ago

0.1.2

1 year ago

0.1.1

1 year ago

0.1.0

1 year ago

0.1.4-alpha

1 year ago

0.1.3-alpha

1 year ago

0.1.2-alpha

1 year ago

0.1.1-alpha

1 year ago

0.1.0-alpha

1 year ago