开始
在React.js中可以看到Hooks导入的代码:
import {
useCallback,
useContext,
useEffect,
useImperativeMethods,
useLayoutEffect,
useMemo,
useReducer,
useRef,
useState,
} from './ReactHooks';
那么我们就来看看具体的代码是怎样的:
function resolveDispatcher() {
const dispatcher = ReactCurrentOwner.currentDispatcher;
return dispatcher;
}
export function useState<S>(initialState: (() => S) | S) {
const dispatcher = resolveDispatcher();
return dispatcher.useState(initialState);
}
export function useEffect(
create: () => mixed,
inputs: Array<mixed> | void | null,
) {
const dispatcher = resolveDispatcher();
return dispatcher.useEffect(create, inputs);
}
export function useContext<T>(
Context: ReactContext<T>,
observedBits: number | boolean | void,
) {
const dispatcher = resolveDispatcher();
// dev code
return dispatcher.useContext(Context, observedBits);
}
以上就是三个最常用的Hooks在React中的源码,可见他们也跟其他React的API一样,只管定义,不管实现。他们都调用了ReactCurrentOwner.currentDispatcher.xxx对应的方法。那么这个ReactCurrentOwner.currentDispatcher是啥呢?
在我们执行renderRoot开始渲染的时候,我们会设置这个值:
import {Dispatcher} from './ReactFiberDispatcher';
if (enableHooks) {
ReactCurrentOwner.currentDispatcher = Dispatcher;
}
并且在离开renderRoot的时候设置为null。那么这个Dispatcher又是啥呢?
// ReactFiberDispatcher.js
import {readContext} from './ReactFiberNewContext';
import {
useCallback,
useContext,
useEffect,
useImperativeMethods,
useLayoutEffect,
useMemo,
useReducer,
useRef,
useState,
} from './ReactFiberHooks';
export const Dispatcher = {
readContext,
useCallback,
useContext,
useEffect,
useImperativeMethods,
useLayoutEffect,
useMemo,
useReducer,
useRef,
useState,
};
OK,Hooks方法的源头代码找到了。需要注意的是Hooks只有FunctionalComponent被更新的时候才会被调用,所以我们肯定需要关心一下FunctionalComponent的更新过程。
function updateFunctionComponent(
current,
workInProgress,
Component,
nextProps: any,
renderExpirationTime,
) {
const unmaskedContext = getUnmaskedContext(workInProgress, Component, true);
const context = getMaskedContext(workInProgress, unmaskedContext);
let nextChildren;
prepareToReadContext(workInProgress, renderExpirationTime);
prepareToUseHooks(current, workInProgress, renderExpirationTime);
nextChildren = Component(nextProps, context);
nextChildren = finishHooks(Component, nextProps, nextChildren, context);
// React DevTools reads this flag.
workInProgress.effectTag |= PerformedWork;
reconcileChildren(
current,
workInProgress,
nextChildren,
renderExpirationTime,
);
return workInProgress.child;
}
可以看到这里增加了三个方法调用:prepareToReadContext,prepareToUseHooks,finishHooks。后两个都明显跟Hooks有关,而第一个是读取新的Context API的,因为在Hooks中有读取Context的操作,所以增加这个就无可厚非了。我们看一下prepareToUseHooks和finishHooks分别做了什么。
prepareToUseHooks
这个方法来自ReactFiberHooks.js,明显只是初始化一些模块内的全局变量。
export function prepareToUseHooks(
current: Fiber | null,
workInProgress: Fiber,
nextRenderExpirationTime: ExpirationTime,
): void {
renderExpirationTime = nextRenderExpirationTime;
currentlyRenderingFiber = workInProgress;
firstCurrentHook = current !== null ? current.memoizedState : null;
}
finishHooks
相比之下finishHooks则复杂很多,一进来就有一个while循环,这个循环是因为Hooks提供了我们一个功能:如果在一次更新中(也就是调用FunctionalComponent的过程中)如果直接调用了类似setState的Hooks API产生了新的更新,则会在当前的渲染周期中直接执行更新。这个大家可以先了解一下,后面我们具体讲Hooks的实现会再具体讲到。
后面设置了renderedWork.updateQueue,就非常类似于HostComponent和ClassComponent了,本来FunctionalComponent在commit阶段是完全没有更新的,但是现在Hooks给了他产生side effect的能力,所以这就是记录这些side effect的queue。
后面就是初始化全局变量了,就先不多讲了。
export function finishHooks(
Component: any,
props: any,
children: any,
refOrContext: any,
): any {
while (didScheduleRenderPhaseUpdate) {
didScheduleRenderPhaseUpdate = false;
numberOfReRenders += 1;
// Start over from the beginning of the list
currentHook = null;
workInProgressHook = null;
componentUpdateQueue = null;
children = Component(props, refOrContext);
}
renderPhaseUpdates = null;
numberOfReRenders = 0;
const renderedWork: Fiber = (currentlyRenderingFiber: any);
renderedWork.memoizedState = firstWorkInProgressHook;
renderedWork.expirationTime = remainingExpirationTime;
renderedWork.updateQueue = (componentUpdateQueue: any);
const didRenderTooFewHooks =
currentHook !== null && currentHook.next !== null;
renderExpirationTime = NoWork;
currentlyRenderingFiber = null;
firstCurrentHook = null;
currentHook = null;
firstWorkInProgressHook = null;
workInProgressHook = null;
remainingExpirationTime = NoWork;
componentUpdateQueue = null;
return children;
}
接下去我们就来看具体的Hooks API的实现。
results matching ""
No results matching ""
扫码添加Jokcy,更多更新更优质的前端学习内容不断更新中,期待与你一起成长!