regionjs / rfcs

0 stars 0 forks source link

基于 region-core api,实现对于共享的单个加载数据的控制 #2

Closed dancerphil closed 2 years ago

dancerphil commented 5 years ago

Prop 架构

在 region-core 第一次在组内介绍之后,@otakustay 问了我一个问题,他问你觉得 load(key, ...) 这个 key 是必须的吗?

当时,我认为是的,而且是必须的。相比于 mapStateToProps 内经常显得非常繁复的 selector,一个 key 是一种非常好的简略。

在使用 region-core 开发的过程中,我也不断的反思这个答案,从学术的角度,redux action 有其学术支撑。而这个在 region-core 中存在的 keyredux 的延伸,这看起来没有问题。但这个 key 确实是可以省去的。

暂时不管这样好不好,首先给出实现,我把这个新的对象取名为 Prop

Prop 使用和 Region 非常类似的原理,是 Region 的简化。而原先一个 region 内有多个通过 key 相互区别的 prop,现在 prop 可以单独进行管理:

const prop = new Prop({
  ...
})

// will get
const { set, loadBy, ..., useValue, useProps, getValue, getProps} = prop;

const App = ({id}) => {
  const result = prop.useValue();
  const { loading, error, fetchTime, result, results, id } = prop.useProps();
  // result always comes from results[id]
  useEffect(
    () => {
      /* 1. switch id
       * 2. load, set loading = true
       * 3. waiting for promise.resolve
       * 4. set loading = false & results[id] = nextResult
       */
      prop.loadBy(fetch)({id});
    },
    [id]
  );
  if (loading) {
    return 'loading...';
  }
  return <div>{results}</div>;
};

Prop 架构中不能实现的部分

这些变化中,会变差的部分如,框架无法帮你进行 loading, fetchTime, error 的计算

const { loading, fetchTime, error } = region.useProps(['a', 'b']);

// 需要转变为
const { loading: loadingA, fetchTime: fetchTimeA, error: errorA } = propA.useProps();
const { loading: loadingB, fetchTime: fetchTimeB, error: errorB } = propB.useProps();
const loading = loadingA || loadingB;
const fetchTime = min(fetchTimeA, fetchTimeB);
const error = [errorA?.message, errorB?.message].join(', ');

如果是 class componentconnect 带来的嵌套更长了,并且会丢失 useProps 里几乎所有的信息,基本上这个接口不再会有(这也是之前 key 不能省略的一个思想局限):

region.connect(['a', 'b', 'c'])(Component);

// 需要转变为
const enhance = compose(
  propA.connect('a'), // it must be a name or it can not be connected
  propB.connect('b'),
  propC.connect('c'),
);

其他的部分没有变得更差。

Prop 带来的好处

它的好处也比较显然,省去了 key 之后,开发者可以以更小的维度组织数据,他也可以准确的即插即用,丝毫不关心其他的 prop 对自己的影响(在 region 中如 loading 这类你希望它们互相影响的部分, 在 prop 中需要开发者自己定义影响的过程)

同时,它带来了重构友好,思奇在 review 的时候,觉得这个 key 作为一个字符串不是很好追踪,在较大的,且注重重构能力的应用中,可能需要把 keys 抽出来(类似 redux actions 一样)。如果要这么做,key 的命名会是一个新的约定,比如:

const { user } = region.useProps(USER);
const { user } = region.useProps($user);
const { user } = region.useProps(_user);
const { user } = region.useProps(userKey);

省去 key 自然就解决了这个问题。

同时,会保证开发者在 regionprop 之间相互切换是快的,在 propcontext 之间相互切换也是快的,这会使开发者拥有从小到大一系列数据管理选择:

在多个数据管理方案之间迁移

这个 part 解答以下问题:

是否需要在一个项目引入所有的方案?如何在不同的方案间迁移?什么时候需要迁移?提供什么接口来迁移?成本多高?

现在在内部使用 region 的经验,可能 prop 可以满足大部分情况。如果要考虑在怎样的维度上拆分 region,极端一些的就是说任何情况都拆,就变成了 prop

dancerphil commented 5 years ago

重命名 Entity => Prop ,由于 Prop 处理 Entity normalize 的问题,不再强调这方面的功能。