1 - hooks api 的组织结构
在看每个具体的 hook 实现之前,我们先来看下 hooks 整体的组成。先从 React 的入口文件开始
- /packages/react/src/React.js - 入口文件的作用是暴露 React 中向外提供的模块,可以很容易的找到 hooks是来自同目录下的 ReactHooks.js 文件
2 - 认识ReactCurrentDispatcher
进入到 ReactHooks.js 文件,把所有 hooks 都浏览一遍之后我们会发现每一个 hook 的实现,都会先调用 resolveDispatcher 来获取 dispatcher,再通过 dispatcher 调用对应的 hook。这样看来 hooks 真正的逻辑都被封装在了 dispatcher 中,那么接下我们就需要探究一下 dispatcher 具体是什么
在 ReactHooks.js 文件中我们可以很容易的检索到 resolveDispatcher 方法的实现,可以看到 dispatcher 其实是来自于同目录下 ReactCurrentDispatcher 模块中
那么我们接着进入到 ReactCurrentDispatcher.js 文件中,我们发现这个模块其实就是单纯暴露一个对象,里面只有一个 current 属性,但并没有 hooks 的踪迹。这说明 ReactCurrentDispatcher 并不是一个固定的 hooks 集合,而是在运行时可能会动态变更的
3 - 追踪ReactCurrentDispatcher
那么对于初次阅读源码的我们,该如何知晓 ReactCurrentDispatcher.current 是在什么阶段被赋值的呢?还是使用最粗暴的方式 - 全局搜素 + 简单的识别,我们先将目标锁定在 ReactFiberHooks.old.js 中的 renderWithHooks 方法,其实这时我们已经追踪到了 react-reconciler 模块中,众所周知的 diff算法 也属于此模块。
回归正题,在 renderWithHooks 方法中,我们可以看到,ReactCurrentDispatcher.current 可能存在两个值,分别是 HooksDispatcherOnMount 和 HooksDispatcherOnUpdate (其实不止,这里我们先忽略广度),通过这两个值的命名我们大概可以猜测,hooks 其实存在多套集合,而这里出现了用于 组件挂载时的 hooks 集合(HooksDispatcherOnMount)以及 用于 组件更新时的 hooks 集合(HooksDispatcherOnUpdate)
4 - dispatcher的真实结构
通过在文件内检索,我们可以找到这两类 dispatcher 初始化的位置,同时还发现了 ContextOnlyDispatcher,HooksDispatcherOnRerender 两个新的 hooks 集合,这里可以暂时忽略
我们先看一下 HooksDispatcherOnMount 的结构,一目了然,HooksDispatcherOnMount 其实就是把所有 mount 时调用的 hook 组合到了一起。形如此类的组合,在 React 中被定义为 dispatcher,接下来我们就可以根据个人的兴趣去了解某个 hook 的具体实现了