Skip to content

这些实现方案都是差不多的,只是API层面的区别。最终选择一个我认为比较合适的方案来实现。

useselector 返回数据,需要配合usedispatch 来修改数据。 connect 需要 mapstate 和 mapdispatch 来配合。

我的useService只能返回方法的集合,不能访问数据,类似usedispatch的作用。因为数据都应该通过props 来提供,这样才能驱动组件更新。 最好是找到类型工具可以禁止访问属性,但是可以继续访问方法。

我的connect起到mapstate 提供props数据的能力,props变化,组件自然重新渲染。 connect 中获取数据也需要调用useService ,此时还没有在ContextProvider之下,所以拿不到对应的服务,第一个方案是在Provider 和 Wrapped Component之间再加一层逻辑层。第二个方案就是declare Providers 应该返回container,可以直接使用这个container 来获取对应的服务。 另一个问题是父组件的props没有变化,是否继续检查子组件。比如修改了一个数组中一个对象的name属性,这属于mutable 修改,那么列表组件不会变化,因为数组的引用没有变化。但是列表项组件的props 会发生变化,所以列表项会自动更新嘛?至少在vue 项目中是会自动更新的,不知道react 会怎样。 还有另一种类似useselector 的方案,我的connect只负责绑定container,use service负责返回数据,再加上usedispatch 负责更新数据,如果不使用usedispatch,那么useService就是正常返回service 对像,可以访问数据和方法,现在的问题是怎么知道当前组件依赖哪些数据,可以提供编译时的工具,自动分析数据依赖,当数据变化时自动更新组件,这些逻辑在vue 中是现成的,但是react 则需要自己实现。

方案1

ts
const service = useService(UserService, {
  userId: 'id',
  userName: 'profile.name',
  cityPath: 'profile.address.city',
  multi: ['profile.name', 'profile.address'],
});

这个方案的第二个参数是一个对象selector,通过对象的形式来映射UserService中的字段,最终返回的service只能访问selector包含的属性值。

缺点是使用字符串作为属性路径导致灵活性降低,不同直接通过.操作符选择相应的字段。

方案2

ts
const service = useService(UserService, ['id', 'profile.name']);

这里的service是完整的UserService实例对象,但是在类型上只能访问idprofile.name这两个属性。

缺点是使用字符串作为属性路径导致灵活性降低,不同直接通过.操作符选择相应的字段。

方案3

ts
const service = useService(UserService, user => ({
  userId: user.id,
  userName: user.profile.name,
  city: user.profile.address.city,
}));

这个方案是完全类似react-redux的useSelector版本,以selector的返回值作为service对象。

这里不同的地方是需要将selector的返回值还需要再加上UserService的所有方法整体作为useService的返回值。

此时有个问题是如果调用service.method方法,会导致this错误指向service对象而不是原始UserService实例对象,最终修改的是service上的数据,而不是UserService实例的数据。

另一个问题是需要在现有selector的基础上实现watch功能,也就是监听selector订阅的所有属性值,当属性值变化时从而可以自动更新组件。但是好像不太好实现。

方案4

ts
const service = useService(UserService, user => [
  () => user.id,
  () => user.profile.name,
]);

本意是selector中返回了哪些属性,那么service在类型上也只能访问哪些属性,但实际上无法实现这种类型推导。

所以目前实现的service就是完整的UserService实例对象,可以访问所有属性。
这时如果有些依赖属性没有加到selector中,那么当这些属性变化时,组件是不会自动更新的。

最终的想法是通过eslint-plugin来实现警告规则,类似useEffect的依赖数组的警告规则一样。
或者可以在编译阶段通过分析组件使用了哪些service属性,然后将属性路径作为参数自动加到useService的第2个参数。