React暴露出来的部分Hooks
//packages/react/src/React.js
export {
...
useCallback,
useContext,
useEffect,
useLayoutEffect,
useMemo,
useReducer,
useRef,
useState,
...
}
功能描述
useState
、useReducer
: 状态值相关useEffect
、useLayoutEffect
: 生命周期相关useContext
: 状态共享相关useCallback
、useMemo
: 性能优化相关useRef
: 属性相关
源码
export function useContext<T>(
Context: ReactContext<T>,
unstable_observedBits: number | boolean | void,
): T {
const dispatcher = resolveDispatcher();
...
return dispatcher.useContext(Context, unstable_observedBits);
}
export function useState<S>(
initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
const dispatcher = resolveDispatcher();
return dispatcher.useState(initialState);
}
export function useReducer<S, I, A>(
reducer: (S, A) => S,
initialArg: I,
init?: I => S,
): [S, Dispatch<A>] {
const dispatcher = resolveDispatcher();
return dispatcher.useReducer(reducer, initialArg, init);
}
export function useRef<T>(initialValue: T): {
|current: T|} {
const dispatcher = resolveDispatcher();
return dispatcher.useRef(initialValue);
}
export function useEffect(
create: () => (() => void) | void, deps: Array<mixed> | void | null,
): void {
const dispatcher = resolveDispatcher();
return dispatcher.useEffect(create, deps);
}
export function useLayoutEffect(
create: () => (() => void) | void, deps: Array<mixed> | void | null,
): void {
const dispatcher = resolveDispatcher();
return dispatcher.useLayoutEffect(create, deps);
}
export function useCallback<T>(
callback: T,
deps: Array<mixed> | void | null,
): T {
const dispatcher = resolveDispatcher();
return dispatcher.useCallback(callback, deps);
}
export function useMemo<T>(
create: () => T,
deps: Array<mixed> | void | null,
): T {
const dispatcher = resolveDispatcher();
return dispatcher.useMemo(create, deps);
}
// resolveDispatcher
function resolveDispatcher() {
const dispatcher = ReactCurrentDispatcher.current;
invariant(
...
);
return dispatcher;
}
// ReactCurrentDispatcher
const ReactCurrentDispatcher = {
/** * @internal
* @type {ReactComponent} */
current: (null: null | Dispatcher),
};
其实hooks
的定义都来自dispatcher
,那我们根据Dispatcher
依次去看看他们的实际实现。
Dispatcher
export type Dispatcher = {
|
...
useState<S>(initialState: (() => S) | S): [S, Dispatch<BasicStateAction<S>>],
useReducer<S, I, A>(
reducer: (S, A) => S,
initialArg: I,
init?: (I) => S,
): [S, Dispatch<A>],
useContext<T>(
context: ReactContext<T>,
observedBits: void | number | boolean,
): T,
useRef<T>(initialValue: T): {
|current: T|},
useEffect(
create: () => (() => void) | void,
deps: Array<mixed> | void | null,
): void,
useLayoutEffect(
create: () => (() => void) | void,
deps: Array<mixed> | void | null,
): void,
useCallback<T>(callback: T, deps: Array<mixed> | void | null): T,
useMemo<T>(nextCreate: () => T, deps: Array<mixed> | void | null): T,
...
|};
很明显这就是各种hooks
的定义,但是总归都要是参加到执行的的流程里面去的,函数组件也属于ReactComponent
的一种,他也有mount
和update
阶段。
函数组件Mount阶段
我们在前面提到执行beginWork
函数中,我们发现会有tag
为FunctionComponent
的选项,他会调用updateFunctionComponent
进而调用renderWithHooks
进行更新。
// packages/react-reconciler/src/ReactFiberBeginWork.old.js
function beginWork(
current: Fiber | null, workInProgress: Fiber, renderLanes: Lanes,
): Fiber | null {
const updateLanes = workInProgress.lanes;
switch (workInProgress.tag) {
case FunctionComponent: {
const Component = workInProgress.type; // 组件类型
const unresolvedProps = workInProgress.pendingProps; // 未处理的props
...
return updateFunctionComponent(
current,
workInProgress,
Component,
resolvedProps,
renderLanes,
);
}
case ClassComponent: {
const Component = workInProgress.type;
const unresolvedProps = workInProgress.pendingProps;
const resolvedProps =
workInProgress.elementType === Component
? unresolvedProps
: resolveDefaultProps(Component, unresolvedProps);
// 渲染classComponent
return updateClassComponent(
current,
workInProgress,
Component,
resolvedProps,
renderLanes,
);
}
case HostRoot:
...
case HostComponent:
...
case HostText:
...
}
}
renderWithHooks
export function renderWithHooks<Props, SecondArg>(
current: Fiber | null,
workInProgress: Fiber,
Component: (p: Props, arg: SecondArg) => any,
props: Props,
secondArg: SecondArg,
nextRenderLanes: Lanes,
): any {
// 下一个渲染任务的优先级
renderLanes = nextRenderLanes;
// fiber树
currentlyRenderingFiber = workInProgress;
...
// 重置memoizedState、updateQueue、lanes
workInProgress.memoizedState = null;
workInProgress.updateQueue = null;
workInProgress.lanes = NoLanes;
...
if (__DEV__) {
...
} else {
// 判断mount update阶段
ReactCurrentDispatcher.current =
current === null || current.memoizedState === null
? HooksDispatcherOnMount
: HooksDispatcherOnUpdate;
}
}
- 如果是
mount
阶段,则执行HooksDispatcherOnMount
,之后再执行reconcileChildren
。 - 如果是
update
阶段,则执行HooksDispatcherOnUpdate
,之后再执行reconcileChildren
。
HooksDispatcherOnMount
const HooksDispatcherOnMount: Dispatcher = {
useCallback: mountCallback,
useContext: readContext,
useEffect: mountEffect,
useLayoutEffect: mountLayoutEffect,
useMemo: mountMemo,
useReducer: mountReducer,
useRef: mountRef,
useState: mountState,
...
};
在mount
的情况下,每个hook
是都有自己对应的mountXxx
,useState
的对应的就是mountState
,不过在讲mountState
之前我们要去看一个东西 – type hook
,因为实际的开发当中不可能只用一个hook,多个hook他就会形成链表,保存在fiber的memoizedState上面。相关参考视频讲解:进入学习
type Hook
export type Hook = {
|
memoizedState: any, // 单独的hook唯一值
baseState: any, // 初始state
baseQueue: Update<any, any> | null, // 初始更新队列
queue: UpdateQueue<any, any> | null, // 更新队列
next: Hook | null, // hooks链表下一个指针
|};
那么我们去看看他是如何关联起来的:const hook = mountWorkInProgressHook()
function mountWorkInProgressHook(): Hook {
const hook: Hook = {
memoizedState: null,
baseState: null,
baseQueue: null,
queue: null,
next: null,
};
if (workInProgressHook === null) {
// This is the first hook in the list
// 第一个hook
currentlyRenderingFiber.memoizedState = workInProgressHook = hook;
} else {
// Append to the end of the list
// 往后面添加,形成链表
workInProgressHook = workInProgressHook.next = hook;
}
return workInProgressHook;
}
mountState
function mountState<S>(
initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
// 创建并关联hook链表
const hook = mountWorkInProgressHook();
// 如果是函数入参,则是把函数的执行结果当做入参
if (typeof initialState === 'function') {
// $FlowFixMe: Flow doesn't like mixed types
initialState = initialState();
}
// 把hook的memoizedState变为初始值
hook.memoizedState = hook.baseState = initialState;
// 更新队列
const queue = (hook.queue = {
pending: null,
dispatch: null,
lastRenderedReducer: basicStateReducer,
lastRenderedState: (initialState: any),
});
// 回调
const dispatch: Dispatch<
BasicStateAction<S>,
> = (queue.dispatch = (dispatchAction.bind(
null,
currentlyRenderingFiber,
queue,
): any));
// 返回 memoizedState, dispatch
return [hook.memoizedState, dispatch];
}
根据mountState
我们就知道在写useState
的时候,解构的语法为什么会解构出想要的东西了。
const [mutation, setMutation] = useState(0) // 纯数字作为初始值
const [mutation, setMutation] = useState(()=>handle(1)) // 函数作为初始值,函数的返回值作为初始值
这里我们遗留了一个问题
- 我们知道第二个参数
dispatch
执行的时候会触发渲染更新
,以及二次更新
,那么他是怎么实现的呢?
针对于上述问题,我们必须去看一下dispatch
到底干了什么事情。
const dispatch: Dispatch<
BasicStateAction<S>,
> = (queue.dispatch = (dispatchAction.bind(
null,
currentlyRenderingFiber,
queue,
): any));
dispatchAction
function dispatchAction<S, A>(
fiber: Fiber,
queue: UpdateQueue<S, A>,
action: A,
) {
if (__DEV__) {
...
}
// 获取触发更新的时间
const eventTime = requestEventTime();
// 获取更新优先级
const lane = requestUpdateLane(fiber);
// 创建更新
const update: Update<S, A> = {
lane,
action,
eagerReducer: null,
eagerState: null,
next: (null: any),
};
// 维护链表,在update阶段,如果有更新任务则一直延续
const pending = queue.pending;
if (pending === null) {
// This is the first update. Create a circular list.
update.next = update;
} else {
update.next = pending.next;
pending.next = update;
}
// 加入更新队列
queue.pending = update;
const alternate =