本文为React Hooks 核心原理与实战阅读笔记
React 组件的本质
我们可以把 UI 的展现看成一个函数的执行过程。其中,Model 是输入参数,函数的执行结果是 DOM 树,也就是 View。而 React 要保证的,就是每当 Model 发生变化时,函数会重新执行,并且生成新的 DOM 树,然后 React 再把新的 DOM 树以最优的方式更新到浏览器。
所以我们是否真的有必要使用 class 来作为组件呢?
事实上使用 class 是一种很牵强的做法,React 根本没有用到类的两个重要特性:
- React 组件之间是不会互相继承的。比如说,你不会创建一个 Button 组件,然后再创建一个 DropdownButton 来继承 Button。所以说,React 中其实是没有利用到 Class 的继承特性的。
- 所有 UI 都是由状态驱动的,因此很少会在外部去调用一个类实例(即组件)的方法。要知道,组件的所有方法都是在内部调用,或者作为生命周期方法被自动调用的。在使用类组件的时候,你从不需要去 new 一个对象来实现任何功能。
换句话说,class 并不是最好的组件表现形式,function 才是。
Hooks 的诞生
但是当时有一个局限是,函数组件无法存在内部状态,必须是纯函数,而且也无法提供完整的生命周期机制。
因此我们需要一个机制,能够把一个外部的数据绑定到函数的执行。当数据变化时,函数能够自动重新执行。
于是有了 Hooks。
在 React 中,Hooks 就是把某个目标结果钩到某个可能会变化的数据源或者事件源上,那么当被钩到的数据或事件发生变化时,产生这个目标结果的代码会重新执行,产生更新后的结果。
对于函数组件,这个结果是最终的 DOM 树;对于 useCallback、useMemo 这样与缓存相关的组件,则是在依赖项发生变化时去更新缓存。
另外,有一点需要特别注意,Hooks 中被钩的对象,不仅可以是某个独立的数据源,也可以是另一个 Hook 执行的结果,这就带来了 Hooks 的最大好处:实现逻辑的复用。
Hooks 实现逻辑复用
在之前的 React 使用中,有一点经常被大家诟病,就是非常难以实现逻辑的复用,必须借助于高阶组件等非常复杂的设计模式。但是高阶组件会产生冗余的组件节点,让调试变得困难。不过这些问题可以通过 Hooks 得到了很好的解决。所以如果有人问你 Hooks 有什么好处,那么最关键的答案就是简化了逻辑复用。
在 Hooks 出现之前,高阶组件几乎是 Class 组件中实现代码逻辑复用的唯一方式,其缺点其实比较显然:
- 代码难理解,不直观,很多人甚至宁愿重复代码,也不愿用高阶组件。
- 会增加很多额外的组件节点。每一个高阶组件都会多一层节点,这就会给调试带来很大的负担。
但现在我们可以通过 Hooks 的方式对外部数据进行封装,从而将其变成一个可绑定的数据源。
Hooks 帮助实现关注点的分离
除了逻辑复用之外,Hooks 能够带来的另外一大好处就是有助于关注分离,意思是说 Hooks 能够让针对同一个业务逻辑的代码尽可能聚合在一块儿。这是过去在 Class 组件中很难做到的。因为在 Class 组件中,你不得不把同一个业务逻辑的代码分散在类组件的不同生命周期的方法中。
在过去的 Class 组件中,我们需要在 componentDidMount
中监听事件,在 componentWillUnmount
中解绑事件。而在函数组件中,我们可以把所有逻辑写在一起。