Flutter 真的非 Hooks 不可吗?聊聊我的思考

1,204 阅读5分钟

Flutter 真的非 Hooks 不可吗?聊聊我的思考

前言:别因为“大家说好”就去用

对于三方库,我一贯的态度是:不能因为别人吹得神乎其神你就跟着上。得先搞清楚它好在哪、适不适合你的项目,以及——如果不吃这颗药,你的病是不是真的治不好?

我最近又仔细研究了一下,Hooks 最大的价值其实在于它的声明式状态同步。因为它直接写在 build 方法里,每次重绘都能拿到最新值,这确实非常契合 Flutter 的声明式 UI 逻辑。


Hooks 解决的核心痛点:参数实时同步

1. 表面上的便利:帮开发者“偷懒”

很多人用 Hooks 是为了少写点 dispose 或者 didUpdateWidget 的样板代码。

// 原生方式:逻辑散落在各处,维护起来很头大
class _AnimatedBoxState extends State<AnimatedBox> with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  
  @override
  void initState() {
    super.initState();
    _controller = AnimationController(duration: widget.duration, vsync: this);
    _controller.addStatusListener(_onComplete);
  }
  
  @override
  void didUpdateWidget(AnimatedBox oldWidget) {
    super.didUpdateWidget(oldWidget);
    // 只要参数一多,这里就是一长串的 if 判断,漏写一个就是 Bug
    if (widget.duration != oldWidget.duration) {
      _controller.duration = widget.duration;
    }
    if (widget.onComplete != oldWidget.onComplete) {
      _controller.removeStatusListener(_onComplete);
      _controller.addStatusListener(_onComplete);
    }
  }
  
  @override
  void dispose() {
    _controller.removeStatusListener(_onComplete);
    _controller.dispose();
    super.dispose();
  }
}

// Hooks 方式:一眼看穿逻辑
Widget build(BuildContext context) {
  final controller = useAnimationController(duration: duration);
  useEffect(() {
    controller.addStatusListener(onComplete);
    return () => controller.removeStatusListener(onComplete);
  }, [onComplete]);
}

这种差异在写基础 UI 组件时特别明显,因为这类组件参数多,手动同步状态简直是噩梦。

但说实话,didUpdateWidget 里面如果参数特别多,封装个 class 就行了,这种参数爆多需要更新的场景真的很少。

2. 核心竞争力:声明式的“值映射”

Hooks 真正厉害的地方,是它把“创建”和“更新”统一了。

  • 原生模式(打补丁)initState 负责生,didUpdateWidget 负责改。你得像个保姆一样,显式地告诉控制器:“喂,外面的参数变了,你赶紧也更新一下”。万一哪天新加个参数忘了同步,逻辑就断层了。
  • Hooks 模式(声明式) :你只需要告诉系统“我要用这个参数”,Hooks 内部会自动判断是该初始化还是该更新属性。
// 声明即同步:管你传进来的是新值还是旧值,我拿到的永远是正确的
final controller = useAnimationController(duration: duration);

3. 复用逻辑:Mixin 还是 Hooks?

首先要说句公道话:Mixin 完全能搞定逻辑复用。 Flutter 官方到处都在用 Mixin(比如 TickerProviderStateMixin),它很成熟。

但两者的代码组织哲学完全不同:

特性MixinHooks
思维方式面向对象(混入/继承)函数式(组合/调用)
状态管理将逻辑“塞进”类里,手动协调将逻辑封装成“函数”,自动管理
易用性适合习惯 OOP 的人适合需要频繁组合逻辑的场景

他们仅仅是实现思想不同,逻辑复用上 mixin 和 hooks 都可以。


为什么 Flutter 没像 React 那样“非 Hooks 不可”?

React 拥抱 Hooks 是因为类组件实在太难用了,但 Flutter 环境不太一样:

  1. Dart 没有 this 的坑:JS 的 this 绑定能让人抓狂,而 Dart 里的方法引用非常直观,没那么多历史包袱。
  2. 生命周期相对清晰:Flutter 的 initStatedidUpdateWidget 职责分明。虽然写起来繁琐,但逻辑不乱。
  3. 心智负担:Hooks 有一套严格的“潜规则”(不能写在 if 里,不能写在循环里)。对于习惯了声明式 Widget 的人来说,这种“顺序敏感”的逻辑确实需要点时间适应。

Compose 和 SwiftUI:天生就是 Hooks 思维

有意思的是,Jetpack Compose 和 SwiftUI 这两个后起之秀,从设计之初就深度整合了 Hooks 的核心思想。

Jetpack Compose (Kotlin)

@Composable 
fun AnimatedBox(duration: Duration, onComplete: () -> Unit) {
    // 直接在 Composable 函数里声明状态,自动跟踪依赖 
    val animatable = remember { Animatable(0f) } 
    // LaunchedEffect 相当于 useEffect,依赖变化时自动重启     
    LaunchedEffect(duration, onComplete) {
    
        animatable.animateTo(1f, animationSpec = tween(duration)) 
                onComplete() 
            } 
    } 

SwiftUI (Swift)

struct AnimatedBox: View { 
    let duration: Double let onComplete: () -> Void 
    // @State 自动管理状态生命周期 
    @State private var progress: CGFloat = 0 
    var body: some View {
        Rectangle() .onAppear {
            withAnimation(.easeIn(duration: duration)) {
                progress = 1.0 
                }
   }
   // onChange 监听依赖变化,类似 useEffect 
   .onChange(of: duration) {  
       newValue in
   
   // 自动响应参数变化 
   
           }
       }
} 

为什么它们天然适合 Hooks?

  1. 纯函数设计:Compose 和 SwiftUI 的组件都是纯函数(Composable/@ViewBuilder),没有类的包袱,状态管理天然就是"声明式"的。
  2. 编译器加持:Kotlin 的编译器插件和 Swift 的 Property Wrapper 能在编译期做很多魔法,让 Hooks 的使用体验更丝滑(比如自动追踪依赖)。
  3. 没有历史包袱:它们不需要像 Flutter 那样兼容已有的 StatefulWidget 体系,可以从零开始设计最适合声明式 UI 的 API。

Flutter 需要一次大版本进化,从 class Widget 进化到 function Widget 编译器上再增加些支持,才能完美契合 hooks

什么时候该上 Hooks?

强烈建议用的时候:

  • didUpdateWidget 里面有一堆参数要更新。
  • 就是喜欢追潮流,追时尚,挡不住

可以不用的时候:

  • 业务逻辑都在 ViewModel 里:如果你用了 Provider/Bloc 并且 UI 层只是简单渲染,Hooks 意义不大。
  • 简单的展示页面:几个文字图片,搞 Hooks 属于杀鸡用牛刀。
  • 团队还没准备好:如果大家对函数式编程不感冒,强行引入只会增加沟通成本。

最后的结论

Hooks 并不是“更好”的替代品,它只是提供了一种不同的解题思路