前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[ - Flutter 状态篇 redux - ] StoreConnector还是StoreBuilder,让distinct把好关

[ - Flutter 状态篇 redux - ] StoreConnector还是StoreBuilder,让distinct把好关

作者头像
张风捷特烈
发布2023-09-01 12:58:17
1740
发布2023-09-01 12:58:17
举报

很多天没发文了,今天翻翻源码,发现解决一个困扰我的问题:redux中的StoreConnector还是StoreBuilder似乎都可以定点刷新,控制粒度。那它们有什么区别呢?在官方样例中基本都用StoreConnector包裹一个组件的最顶层,并且特别是在StoreBuilder源码中注释让我心里咯噔一下:我偏爱的StoreBuilder竟然是下下签,推荐使用StoreConnector。喵了个咪,重构一下世界观。

代码语言:javascript
复制
/// Build a Widget by passing the [Store] directly to the build function.
///
/// Generally, it's considered best practice to use the [StoreConnector] and to
/// build a `ViewModel` specifically for your Widget rather than passing through
/// the entire [Store], but this is provided for convenience when that isn't
/// necessary.

既然不服那就来测:


1.StoreConnector 对战 StoreBuilder 第一回合
1.1:三件套先搞上
代码语言:javascript
复制
class CountState {
  final int counter; //计时器数字
  CountState(this.counter);

  factory CountState.init([int counter]) => CountState(counter ?? 0);
}

//行为
class ActionCountAdd {}

//处理器
var countReducer =
TypedReducer<CountState, ActionCountAdd>((state, action) {
 var counter;
 if(action is ActionCountAdd) counter = state.counter + 1;
  return CountState(counter);
});

1.2:把老爹先认着
代码语言:javascript
复制
void main() => runApp(Wrapper(child: MyApp(),));

class Wrapper extends StatelessWidget {
  final Widget child;

  Wrapper({this.child});

  final store = Store<CountState>(
    //初始状态
    countReducer, //总处理器
    initialState: CountState.init());//初始状态


  @override
  Widget build(BuildContext context) {
    return StoreProvider(store: store, child: child);
  }
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return  MaterialApp(
              title: 'Flutter Demo',
              theme: ThemeData(primarySwatch: Colors.blue,), 
              home: MyHomePage(title: 'Flutter Demo Home Page'),
            );
  }
}

1.3:视图模型

StoreConnector通常通过一个ViewHolder与仓库Store进行关联,然后将状态资源提供给视图

代码语言:javascript
复制
class CountViewModel {
  final int count;//数字
  final VoidCallback onAdd;//点击回调

  CountViewModel(
      {@required this.count, @required this.onAdd });

  static CountViewModel fromStore(Store<CountState> store) {
    return CountViewModel(
      count: store.state.counter,
      onAdd: () => store.dispatch(ActionCountAdd()),
    );
  }
}

1.4 使用StoreConnector

可见每次都会使用只会走StoreConnector中的builder内部,并不会执行_MyHomePageState,如果将StoreConnector定点进行连接就可以缩小更新粒度

代码语言:javascript
复制
class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

  @override
  Widget build(BuildContext context) {
    print("MyHomePage--------builder");
    return StoreConnector<CountState, CountViewModel>(
        converter: CountViewModel.fromStore,
        builder: (context, vm) {
          print("StoreConnector--------builder");
          return Scaffold(
              appBar: AppBar(
                title: Text(widget.title),
              ),
              body: Center(
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: <Widget>[Text(
                              'You have pushed the button this many times:',
                              style: TextStyle(
                                  fontSize: 18),
                  ),
                     Text('${vm.count}',
                        style: Theme.of(context).textTheme.display1,
                    ),
                  ],
                ),
              ),
              floatingActionButton: FloatingActionButton(
                onPressed: vm.onAdd,
                tooltip: 'Increment',
                child: Icon(Icons.add),
              ),
            );
        });
  }
}

1.5 使用StoreBuilder

StoreBuilder直接连接到Store,用起来比较简单,能打(触发事件)能抗(获取数据)。从表现上来看也是同样优秀。用起来似乎是StoreBuilder更加简单。

代码语言:javascript
复制
class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

  @override
  Widget build(BuildContext context) {
    print("MyHomePage--------builder");
    return StoreBuilder<CountState>(
        builder: (context, store) {
          print("StoreBuilder--------builder");
          return Scaffold(
            appBar: AppBar(
              title: Text(widget.title),
            ),
            body: Center(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[Text(
                  'You have pushed the button this many times:',
                  style: TextStyle(
                      fontSize: 18),
                ),
                  Text('${store.state.counter}',
                    style: Theme.of(context).textTheme.display1,
                  ),
                ],
              ),
            ),
            floatingActionButton: FloatingActionButton(
              onPressed: ()=> store.dispatch(ActionCountAdd()),
              tooltip: 'Increment',
              child: Icon(Icons.add),
            ),
          );
        });
  }
}

2.StoreConnector 对战 StoreBuilder 第二回合
2.1 场景布置

添加一个ActionCountNone的行为,点击数字触发,数字状态保持原样。点三下自加事件,两次不加事件。查看结果

代码语言:javascript
复制
class CountState {
  final int counter; //计时器数字
  CountState(this.counter);

  factory CountState.init([int counter]) => CountState(counter ?? 0);
}

//切换主题行为
class ActionCountAdd {}
class ActionCountNone {}

//切换主题理器
var countReducer =
TypedReducer<CountState, ActionCountAdd>((state, action) {
 var counter;
 if(action is ActionCountAdd) counter = state.counter + 1;
 if(action is ActionCountNone) counter = state.counter ;
  return CountState(counter);
});

2.2:StoreBuilder出战

可见状态量未改变,但界面刷新了。虽然定点的刷新可以控制粒度,但粒度小,StoreBuilder就会用得多,虽小,但状态量不变,刷新了也是事实。

代码语言:javascript
复制
class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

  @override
  Widget build(BuildContext context) {
    print("MyHomePage--------builder");
    return StoreBuilder<CountState>(
        builder: (context, store) {
          print("StoreBuilder--------builder");
          return Scaffold(
              appBar: AppBar(
                title: Text(widget.title),
              ),
              body: Center(
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: <Widget>[Text(
                              'You have pushed the button this many times:',
                              style: TextStyle(
                                  fontSize: 18),
                  ),
                    InkWell(
                      onTap: ()=> store.dispatch(ActionCountNone()),//<--不加
                      child: Text('${store.state.counter}',
                        style: Theme.of(context).textTheme.display1,
                      ),
                    ),
                  ],
                ),
              ),
              floatingActionButton: FloatingActionButton(
                onPressed: ()=> store.dispatch(ActionCountAdd()),
                tooltip: 'Increment',
                child: Icon(Icons.add),
              ),
            );
        });
  }
}
2.2:StoreConnector出战

StoreConnector冷笑:哥们,瞧我的 这里重写了CountViewModel的判等,可见当CountViewModel状态量不变时,界面不刷新 如果想让他刷新,可以控制distinct属性。所以StoreConnector似乎更胜一筹。

代码语言:javascript
复制
class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

  @override
  Widget build(BuildContext context) {
    print("MyHomePage--------builder");
    return StoreConnector<CountState, CountViewModel>(
        distinct: true,
        converter: CountViewModel.fromStore,
        builder: (context, vm) {
          print("StoreConnector--------builder");
          return Scaffold(
              appBar: AppBar(
                title: Text(widget.title),
              ),
              body: Center(
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: <Widget>[Text(
                              'You have pushed the button this many times:',
                              style: TextStyle(
                                  fontSize: 18),
                  ),
                    InkWell(
                      onTap: vm.onNone,
                      child: Text('${vm.count}',
                        style: Theme.of(context).textTheme.display1,
                      ),
                    ),
                  ],
                ),
              ),
              floatingActionButton: FloatingActionButton(
                onPressed: vm.onAdd,
                tooltip: 'Increment',
                child: Icon(Icons.add),
              ),
            );
        });
  }
}

class CountViewModel {
  final int count;//数字
  final VoidCallback onAdd;//点击回调
  final VoidCallback onNone;//点击回调

  CountViewModel(
      {@required this.count, @required this.onAdd,@required this.onNone,  });

  static CountViewModel fromStore(Store<CountState> store) {
    return CountViewModel(
      count: store.state.counter,
      onAdd: () => store.dispatch(ActionCountAdd()),
      onNone: () => store.dispatch(ActionCountNone()),
    );
  }
  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
          other is CountViewModel &&
              runtimeType == other.runtimeType &&
              count == other.count;

  @override
  int get hashCode => count.hashCode;
}

到这你似乎又要说谁好谁坏了,那我只有呵呵了。没有好坏,只要适合和不适合,StoreConnector需要ViewModel对于一些较大块的组件可以使用。如果就一两个字段或是犄角旮旯里的小组件,StoreBuilder也是很精简的,刷一下就刷呗,犯不着为了一分钱去搬砖。知道它们在干什么最重要,而不是评论好坏。否则只会沦落键盘侠和喷嘴...还不如来我的Flutter群里交流技术。手动搞笑。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-11-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.StoreConnector 对战 StoreBuilder 第一回合
    • 1.1:三件套先搞上
      • 1.2:把老爹先认着
        • 1.3:视图模型
          • 1.4 使用StoreConnector
            • 1.5 使用StoreBuilder
            • 2.StoreConnector 对战 StoreBuilder 第二回合
              • 2.1 场景布置
                • 2.2:StoreBuilder出战
                  • 2.2:StoreConnector出战
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档