Flutter 需要 Hooks 吗?

63 阅读4分钟

原作者并不喜欢 Hooks,认为它们与 Flutter 的设计哲学并不契合,并借此推销他自己的解决方案(state_lifecycle_observer):

截了当地说,我觉得 Flutter 生态系统已经被 Hooks “毒害”了。甚至现在的 AI 工具也会把 Hooks 推荐给初学者,搞得好像它是什么标准架构一样。刚起步的时候我也不懂,只看 pub.dev 上的点赞数(stars);哪个包火,我就用哪个。Riverpod 和 Hooks 我都试过,我觉得都不好——不过这里我只针对 Hooks 展开。 Hooks 不适合 Flutter 的原因在于:它是函数式编程导向的,这与基于面向对象(OOP)的 class 组件(Widgets)天然不匹配。如果你个人偏好这种风格,把它当成一种“语法糖”来用没问题,但不该打着“解决状态复用”或“消除模板代码”的幌子去推广它。这样做只会误导初学者。

这段“生态毒害论”引来了 Remi Rousselet(flutter_hooksproviderriverpod 的作者)的一番回应,语气倒是非常客气:

你这完全是在一个人打一场不存在的仗。其实用 Hooks 的人并不多,而且几乎没人主张把 Hooks 塞进 Flutter 官方框架里。甚至作为 flutter_hooks 的作者,我本人也不认为 Hooks 是 Flutter 的完美方案。 你链接里的那个 Issue,更多是在探讨“Hooks 试图解决的那些痛点”,而不是在说“我们需要 Hooks”。 我个人觉得,如果不从语言(Dart)或框架层面做改动,是很难有完美方案的。你的提议确实不错,但语法还是显得太重了,而且也有它自己的坑(特别是当一个“观察对象”依赖于随时间变化的外部数值时,情况会变得非常棘手,尤其是如果这些变化发生在 Widget 生命周期之外的话)

我以前学过 Hooks,但自从用了 GetX 之后,因为它完全消灭了对 StatefulWidget 的需求,自然也就没 Hooks 什么事了,所以我已经把它忘得一干二净。

咱们来复习一下: 当 StatefulWidget 的存在不是为了管理复杂的业务状态,而仅仅是为了初始化和销毁资源(比如 TextEditingControllerFocusNode 之类)时,Hooks 就是一种更简洁的替代方案。

举个例子:如果我们只是想在视图里加一个简简单单的 TextField,通常必须得把整个视图变成 StatefulWidget,然后在 initState 里创建控制器,最后还得在 dispose 里把它销毁掉。

class MyWidget extends StatefulWidget {
  @override
  State<MyWidget> createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  late TextEditingController _controller;
  
  @override
  void initState() {
    super.initState();
    _controller = TextEditingController();
  }
  
  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
  
  @override
  Widget build(BuildContext context) {
    return Scaffold (
        body: Center (
            child: TextField(controller: _controller), 
     ));
  }
}

如果用 Hooks,我们就可以写成这样:

class MyWidget extends HookWidget {
  @override
  Widget build(BuildContext context) {
    final _controller = useTextEditingController();
    return Scaffold (
        body: Center (
            child: TextField(controller: _controller), 
     ));
  }
}

useTextEditingController 这个方法能直接帮你创建 TextEditingController 实例,并处理好后续的销毁工作。这非常对我的胃口,完美符合 KISS 原则(简单就是美)。😘

所以,我不吃“Hooks 不适合 Flutter 是因为它是函数式导向,而 Dart 是面向对象”这一套。这说法也许没错,但谁在乎呢?

只要别逼着我去读那些“函数式编程天才”写出来的、长成下面这样的代码就行:

filter(...).map(...).filter(...).map(...).reduce(...).slice....

……只要代码别写成那样,我对函数式编程其实没啥意见。

Hooks 的问题在于,它们解决的并不是真正的痛点。

真正的问题出在人们没有使用“正确的架构”(比如 MVVM)以及具备生命周期感知能力的 ViewModel。相反,大家都在用那种所谓的“整洁架构”搭配Riverpod(这玩意儿我也不喜欢 😎)。只有在这种情况下,你才不得不求助于 Hooks。

那什么是“具备生命周期感知能力”的 ViewModel? Flutter 的四大主流状态管理方案中,有三个——Provider、BLoC 和 GetX——都拥有这类 ViewModel。

  • ChangeNotifier 类有一个 dispose 方法,当 ChangeNotifierProvider 从组件树中卸载时,它就会被自动调用。
  • Bloc 有一个 close 方法,同样会在 BlocProvider 卸载时触发。
  • GetxController 则有一个 onClose 方法,会在相关视图卸载时被调用。

**因此,在这些方案中,我们都可以像下面这样写代码:

final class MyViewModel extends GetxController {  
final textController = TextEditingController();  
  
void onClose() {  
    textController.dispose();  
    }  
}

根本不需要什么 Hooks。

所以说,Hooks 只有在搭配 Riverpod 或类似的状态管理方案时才算刚需,而这些方案往往并不走 MVVM 架构那套路子。

顺便提一下,利用 Lifecycle 组件,我们可以非常轻松地创建出具备生命周期感知能力的 ViewModel。在这种 ViewModel 内部,你可以随心所欲地使用任何状态管理方案,比如 Signals,甚至你要用 Riverpod 也行。

就这样。感谢阅读!