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
泛型化能够更好的降低者之间的耦合度,减少代码的冗余,增加复用性,变得不那么僵硬、那么固定化,能够更好的发挥连接器的配置性。
转载自:https://juejin.cn/post/7020654999763959821