Hooks 二三事 (2) | 可视化对比引入 Hooks 前后
上一篇文章中,提及 「Hooks 就是让你更容易做你已经在做的事情」。那么”容易“二字体现在哪里呢?今天要分享的这篇文章就从可视化角度,量化用 Hooks 重构组件前后的代码量,并探讨在思维模式上的转变。
原文链接:https://wattenberger.com/blog/react-hooks
一年前,React 团队提出了 Hooks,改变了社区内很多开发者的开发习惯。网上有大量的文章在介绍 Hooks 的基本功能 ,但本文将探讨 ”类组件“ 与 ”函数组件 + Hooks“ 两种开发模式下思维方式的转变。
生命周期
-
在类组件中,我们会在特定的生命周期函数中进行更新操作。
-
在函数组件中,我们使用
useEffect
钩子在必要的生命周期中执行代码。
通过这个示例,你可能会想:”好吧,useEffect
就是提供了一种在生命周期中执行代码的新方式。“ 然而,这个结论是不够全面的。接下来一起看看这个更为完善的例子,比如有一个计算开销很大的 getDataWithRange()
函数,该函数根据指定的 dataRange
返回筛选后的数据。这该怎么办呢?为了减少重复的计算开销,我们会希望把它存储到 state
对象中,当且仅当 dataRange
发生改变时才更新它。
- 使用生命周期中的事件,我们需要在某一个点上处理所有的变化。我们的思考过程是这样的:“当组件开始加载和参数发生变化时(特指
dataRange
),更新data
状态”。 - 在函数组件中,我们需要思考的是哪些数据需要保持同步,思考的过程会是:“
data
需要与dataRange
保持同步” 。
这样的思考过程是不是简化了许多。但是,上述的例子中,我们依旧是在类组件的思想框架中思考问题。我们将 data
存储到 state
中,以此来避免每次组件更新中重新计算 data
值。现在,我们完全可以不用 state
!Hooks 提供了 useMemo
方法来解决这个问题,当且仅当依赖数组内的任一元素发生变化时,才重新计算 data
值。
更新上下文
是否还觉得不够直观?那就再看看这个更加复杂的例子吧,可以让你更深刻的体会到两种思考模式的认知负担差距。
假设我们在组件中有很多需要实时计算的值,它们依赖于不同的 props
。比如,我们需要计算:
-
data
:当dataRange
改变时更新; -
dimesions
:当margins
改变时重新计算; -
scales
:当data
改变时需要同步更新。
在函数组件中,我们能够快速快速逻辑关系,比如“让 dimesions
与 margins
保持同步”。
即使在这样简单的示例中类组件也显得特别笨重。这是因为我们有很多声明类的代码,用来解释如何使代码中的变量与 props
和 state
保持同步,而在函数组件中, 我们只需要关注哪些数据需要保持同步。
注意,我们在代码中使用了很多次
useMemo()
钩子,以此拉近依赖项和副作用的“距离”
宽松地定义状态
再加点逻辑,如果 scales
需要根据图表中的 dimension
改变,又该怎么做呢?
- 在类组件中,我们需要对比
prevState
和当前的state
; - 在函数组件中,hooks 并不关心依赖数组中的
margins
,dimesion
是来自于state
还是props
,还是两者都不是,对它而言,值就是一个值。
总结
代码简洁
代码量少不一定容易阅读,让保持组件代码的简洁一定是一种胜利。接下来让我们快速比较一下上述示例的代码行数吧:
图4<center>从左到右为上述的 5 个示例代码量,左边的柱状长度表示「类组件」代码行数,右边的柱状长度表示同样功能下的「函数组件+ Hooks」代码行数</center>
函数组件的平均代码行数是类组件的 46.1%。另外,再比较一下核心版本中主要的代码差异:
图5堆叠图中每层的颜色与示例动图中的连接带颜色保持一致
可以看到我们是如何按功能逻辑将相关代码统一到一起的,而不是分散在多个生命周期事件中。
逻辑复用
Hooks 赋予了我们在组件之间共享功能逻辑的能力,而不需要去操作组件的生命周期事件。当然,使用类组件的编写模式时,你也可以利用高阶组件来实现这一需求,但是高阶组件所带来的麻烦比它创造的价值更多,经常把 render
函数搞得一团糟。
我(Amelia Wattenberger )从参与的众多项目中提取并维护了一个简单的 Hooks 仓库,降低了开发成本。从我的角度来讲,复杂功能逻辑的复用带来的便利包括:
- 不必重复一些通用模式,比如:保证图表的比例与容器一致。在实现上,会是逻辑复杂、行数不少的代码块,如果需要不断在每个图表中实现,将是令人奔溃的。
- KISS (Keep it simple and stupid)——让我在思考的时候能够专注于某一个点,确保我不会漏掉某个重要的环节,比如解绑事件等。
译者言
个人的一些看法:
- 文中使用的案例代码都相对简单,但量化的过程是个不错的方法,我们有没有办法在实际项目中通过打点的方式做类似的比较呢;
- 文中提到的
useMemo
的使用,也许可以作为可视化保持复杂数据同步的一个规范; - Hooks 在功能逻辑复用上面的优势,可以通过建立团队内部的 hooks 仓库发挥出来。