react源码解读之ReactHooks.js

「这是我参与11月更文挑战的第28天,活动详情查看:2021最后一次更文挑战」。

ReactHooks.js导出了很多内置的钩子函数,什么是钩子,读者可以带着这个问题去阅读下面的内容。

以useState钩子函数为例

export function useState<S>(
  initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
  const dispatcher = resolveDispatcher();
  return dispatcher.useState(initialState);
}
复制代码
  • useState这个钩子函数的入参为initialState,也就是初始化状态。
  • 首先通过resolveDispatcher方法找到当前派发器。
  • 然后调用特定生命周期的派发器上的方法,会返回这个函数的调用结果,这个函数调用结果的返回值为数组,数组第一个元素为状态,数组第二个元素为改变状态的函数,也就是其内部会调用setState

解析不同阶段的派发器

function resolveDispatcher() {
  const dispatcher = ReactCurrentDispatcher.current;
  return dispatcher;
}
复制代码

由于react有各个不同生命周期,react在更新组件的状态等各种有效载荷(payload)时,会调用不同的派发器。 resolveDispatcher通过全局变量ReactCurrentDispatchercurrent属性来获取当前的派发器。 这个派发器上包括了react钩子函数的各个内部方法,比如useState。

useState方法内部是如何运行的

function mountState(initialState) {
  const hook = mountWorkInProgressHook();
  hook.memoizedState = hook.baseState = initialState;
  const queue = {
    pending: null,
    interleaved: null,
    lanes: NoLanes,
    dispatch: null,
    lastRenderedReducer: basicStateReducer,
    lastRenderedState,
  };
  hook.queue = queue;
  const dispatch= (queue.dispatch = (dispatchSetState.bind(
    null,
    currentlyRenderingFiber,
    queue,
  )));
  return [hook.memoizedState, dispatch];
}
复制代码

每当你调用useState这个钩子函数时,相当于是调用当前触发器的useState,那么当前触发器的useState有会调用在不同周期阶段的方法。比如挂载完成这个阶段,会调用mountState方法,并将initialState传入。

正道行不通

按顺序解说下来有点绕。那么我们倒序理解它。

bind()方法创建一个新的函数,在bind()被调用时,这个新函数的 this 被指定为bind()的第一个参数,而其余参数将作为新函数的参数,供调用时使用。(摘自mdn)

  1. 首先dispatchSetState方法会获取到当前运行的Fiber织物,整个页面由react的织物编织起来。
  2. 这个方法会将改变这个织物内部状态state这个过程推入更新队列
  3. 通过bind方法,将当前渲染的织物和队列这两个预置参数传给钩子函数的返回值数组的第二个元素。这里很关键,就是通过这个方法,将织物与函数组件钩在一起
  4. dispatch这个变量也就是我们钩子函数要返回的值。这里dispatch变量应用queue的dispatch属性,而queue.dispatch属性又应用了通过bind绑定的函数,因而hook.queue这个对象实际上有一个dispatch这个方法。

暗箱操作

hook和queue这两个内部变量好像只在内部被定义了,它没有供外部使用,内部也没有使用到它。 爆炸,react作者怎么会定义无用的hook和queue变量。 好了,关键代码在下面。

const hook = mountWorkInProgressHook();
// workInProgressHook
复制代码

这个生成hook的函数内部使用了一个全局变量,用来保存当前的hook,这个hook会缓存一些历史内容用以优化性能。将hook.queue属性赋值之后,会有一个loop去调用这个queue里面的方法。 具体可以看看updateWorkInProgressHook这个方法。

感谢读者的阅读,如果觉得对你启发,欢迎动动小手,点一波赞。

猜你喜欢

转载自juejin.im/post/7036234868082081823