fish_redux slots Connector 泛型适配器
制作背景:在使用闲鱼的fish_redux
框架的时候就会遇到每次编辑一个slots
都需要创建一个connector
连接器,这件事使得我非常的不开心。我就想能不能编写一个能够适配不同component
的适配器,接下来就开始思考这个问题。
要实现不同component
使用同一个connector
连接器,使用泛型就非常的适合。
首先我们看到fish_redux
的例子里面:
其中page
里面有slots
槽连接
slots: <String, Dependent<PageState>>{ 'report': ReportConnector() + ReportComponent() }),复制代码
他这里每次使用一个component
都要创建一个connector
连接器,我们就需要根据不同的state
对象将T
(泛型)的适配,我们再来看一看ReportConnector
这个类,这里面有两个state
,分别是当前page
的state
(PageState
)和component
的state
(ReportState
),就需要用到两个 T
、P
(泛型),将对应的state
使用T
、P
(泛型)替换掉。(以下我们用T
(泛型)代表PageState
,P
(泛型)代表ReportState
),ReportConnector
也需要制定两个 T
、P
(泛型)传入进来。
class ReportConnector extends ConnOp<PageState, ReportState> with ReselectMixin<PageState, ReportState> { @override ReportState computed(PageState state) { return ReportState() ..done = state.toDos.where((ToDoState tds) => tds.isDone).length ..total = state.toDos.length; } @override List<dynamic> factors(PageState state) { return <int>[ state.toDos.where((ToDoState tds) => tds.isDone).length, state.toDos.length ]; } @override void set(PageState state, ReportState subState) { throw Exception('Unexcepted to set PageState from ReportState'); } } 改 class ReportConnector<T, P> extends ConnOp<T, P> with ReselectMixin<T, P> { @override P computed(T state) { return ReportState() ..done = state.toDos.where((ToDoState tds) => tds.isDone).length ..total = state.toDos.length; } @override List<dynamic> factors(PageState state) { return <int>[ state.toDos.where((ToDoState tds) => tds.isDone).length, state.toDos.length ]; } @override void set(T state, P subState) { throw Exception('Unexcepted to set PageState from ReportState'); } }复制代码
这里我们再看看这里面的一些方法,其中computed
方法返回的是P
(泛型),但是传入的是T
(泛型),我们怎么从T
(泛型)中拿到这个P
(泛型)呢,这个问题值得思考以下。
首先要解耦的话我们只能通过T
(泛型)去获取这个P
(泛型),那这个T
(泛型)就需要有个P
(泛型),那我们需要在这个T
(泛型)添加一个P
(泛型),添加完了怎么去获取出来呢,这里思考以下。因为T
(泛型)默认继承的是dynamic
类型,不能直接把P
(泛型)取出来,所以就需要继承一个基类,我在这是新建了一个abstract
类为BaseConnector
,并且这里P
(泛型)ReportState
.
abstract class BaseConnector<T>{ ReportState get state => ReportState(); }复制代码
这样ReportConnector
传入T
(泛型)就可以继承BaseConnector
从而取出P
(泛型)
class ReportConnector<T, P> extends ConnOp<T, P> with ReselectMixin<T, P> 改 class ReportConnector<T extends BaseConnector, P> extends ConnOp<T, P> with ReselectMixin<T, P> 复制代码
现在我们在computed
方法中通过T
(泛型)取出P
(泛型),这样我们就获取到了P
(泛型)
P computed(T state) { return state.state; }复制代码
但是如果BaseConnector
这里面编写固定为ReportState
的话也不是很理想,这样就固定话ReportState
这个state
了,我们可以用一个T
(泛型)来代替。再由于slots
槽可以放置多个component
,就不能单一的只获取其中的一种state
,就需要用List
数组来承载多个state
,将其命名为args
。
abstract class BaseConnector<T>{ ReportState get state => ReportState(); } 改 abstract class BaseConnector<T>{ T state; } 再改 abstract class BaseConnector{ List get getArgs => []; }复制代码
然后回到computed
方法上面,我们需要把多个state
都承载出来,对应返回给P
(泛型)
P computed(T state) { return state.state; } 改 P computed(T state) { List args = args ?? state.getArgs; if(args!=null){ // 遍历args数组,并且一一进行类型比较,当类型为P(泛型)类型后就返回对应的state for(Object object in args){ if(object.runtimeType == P){ args = null; return object; } } } args = null; return null; }复制代码
那么返回对应的state
算是完成了,那我们在呢么去更新这个对应的state
呢,来看到set
方法,目前他这个set
方法throw
出了一个异常,说的是需要修改PageState
里面的ReportState
,一开始我们是修改一个使用state.state = subState;
,但是我们需要修改多个,就需要便利args
数组,一一替换对应的state
。
void set(T state, P subState) { throw Exception('Unexcepted to set PageState from ReportState'); } 改 void set(T state, P subState) { state.state = subState; } 再改 void set(T state, P subState) { List args = state.getArgs; if(args!=null){ // 遍历args数组,并且一一进行类型比较,当类型为P(泛型)类型后就替换对应的state for(int i = 0;i < args.length;i++){ if(args[i].runtimeType == P){ args[i] = subState; } } } }复制代码
因为每次修改后list
都会改变,就需要定义一个list
成员变量来保存每次修改后的数组
List args; P computed(T state) { args = args ?? state.getArgs; if(args!=null){ // 遍历args数组,并且一一进行类型比较,当类型为P(泛型)类型后就返回对应的state for(Object object in args){ if(object.runtimeType == P){ args = null; return object; } } } args = null; return null; } void set(T state, P subState) { args = state.getArgs; if(args!=null){ // 遍历args数组,并且一一进行类型比较,当类型为P(泛型)类型后就替换对应的state for(int i = 0;i < args.length;i++){ if(args[i].runtimeType == P){ args[i] = subState; } } } }复制代码
这样的话链接器就算是完成了,修改了几处泛型就解决了每次需要创建connector
连接器的问题
下面是使用方式,无需重复创建connector
连接器,直接在slots
里面添加component
即可使用,只需要加上对应的state
即可。
slots: <String, Dependent<PageState>>{ 'report': ReportConnector() + ReportComponent() }), slots: <String, Dependent<PageState>>{ "report": Connector<PageState,ReportState>() + ReportComponent(), }),复制代码
编写完毕,下面贴上我的代码
connector.dart
abstract class BaseConnector{ List get getArgs => []; } class Connector<T extends BaseConnector, P> extends ConnOp<T, P> with ReselectMixin<T, P> { List args; @override P computed(T state) { args = args ?? state.getArgs; if(args!=null){ for(Object object in args){ if(object.runtimeType == P){ args = null; return object; } } } args = null; return null; } @override void set(T state, P subState) { args = state.getArgs; if(args!=null){ for(int i = 0;i < args.length;i++){ if(args[i].runtimeType == P){ args[i] = subState; } } } } }复制代码
page.dart
class CityPage extends Page<CityState, Map<String, dynamic>> { CityPage() : super( initState: initState, effect: buildEffect(), reducer: buildReducer(), view: buildView, dependencies: Dependencies<CityState>( adapter: null, slots: <String, Dependent<CityState>>{ "header_component": Connector<CityState,HeaderState>() + HeaderComponent(), "tools_component": Connector<CityState,ToolsState>() + ToolsComponent(), "body_component": Connector<CityState,BodyState>() + BodyComponent(), }), middleware: <Middleware<CityState>>[ ],); }复制代码
state.dart
这里也需要实现一下BaseConnector
class CityState implements Cloneable<CityState> , BaseConnector { List<ToolModel> list = <ToolModel>[]; int style = 0; @override CityState clone() { return CityState()..list=list..style=style..getArgs; } @override List get getArgs => [ HeaderState(), ToolsState()..list=list, BodyState(), ]; } CityState initState(Map<String, dynamic> args) { return CityState(); }复制代码
总结:将state
泛型化能够更好的降低者之间的耦合度,减少代码的冗余,增加复用性,变得不那么僵硬、那么固定化,能够更好的发挥连接器的配置性。
作者:RogueYBJ
链接:https://juejin.cn/post/7020654999763959821