react源码解析13.hooks源码
## react源码解析13.hooks源码
#### 视频课程(高效学习):[进入课程](https://xiaochen1024.com/series/60b1b600712e370039088e24/60b1b636712e370039088e25)
#### 课程目录:
[1.开篇介绍和面试题](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b311cf10a4003b634719)
[2.react的设计理念](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b31ccf10a4003b63471a)
[3.react源码架构](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b328cf10a4003b63471b)
[4.源码目录结构和调试](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b32ecf10a4003b63471c)
[5.jsx&核心api](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b334cf10a4003b63471d)
[6.legacy和concurrent模式入口函数](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b33acf10a4003b63471e)
[7.Fiber架构](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b340cf10a4003b63471f)
[8.render阶段](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b348cf10a4003b634720)
[9.diff算法](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b354cf10a4003b634721)
[10.commit阶段](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b360cf10a4003b634722)
[11.生命周期](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b367cf10a4003b634723)
[12.状态更新流程](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b36ecf10a4003b634724)
[13.hooks源码](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b374cf10a4003b634725)
[14.手写hooks](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b37acf10a4003b634726)
[15.scheduler&Lane](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b556cf10a4003b634727)
[16.concurrent模式](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b55ccf10a4003b634728)
[17.context](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b564cf10a4003b634729)
[18事件系统](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b56ccf10a4003b63472a)
[19.手写迷你版react](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b57bcf10a4003b63472b)
[20.总结&第一章的面试题解答](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b581cf10a4003b63472c)
[21.demo](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b587cf10a4003b63472d)
#### hook调用入口
在hook源码中hook存在于Dispatcher中,Dispatcher就是一个对象,不同hook 调用的函数不一样,全局变量ReactCurrentDispatcher.current会根据是mount还是update赋值为HooksDispatcherOnMount或HooksDispatcherOnUpdate
```js
ReactCurrentDispatcher.current =
current === null || current.memoizedState === null//mount or update
? HooksDispatcherOnMount
: HooksDispatcherOnUpdate;
```
```jsx
const HooksDispatcherOnMount: Dispatcher = {//mount时
useCallback: mountCallback,
useContext: readContext,
useEffect: mountEffect,
useImperativeHandle: mountImperativeHandle,
useLayoutEffect: mountLayoutEffect,
useMemo: mountMemo,
useReducer: mountReducer,
useRef: mountRef,
useState: mountState,
//...
};
const HooksDispatcherOnUpdate: Dispatcher = {//update时
useCallback: updateCallback,
useContext: readContext,
useEffect: updateEffect,
useImperativeHandle: updateImperativeHandle,
useLayoutEffect: updateLayoutEffect,
useMemo: updateMemo,
useReducer: updateReducer,
useRef: updateRef,
useState: updateState,
//...
};
```
#### hook数据结构
在FunctionComponent中,多个hook会形成hook链表,保存在Fiber的memoizedState的上,而需要更新的Update保存在hook.queue.pending中
```js
const hook: Hook = {
memoizedState: null,//对于不同hook,有不同的值
baseState: null,//初始state
baseQueue: null,//初始queue队列
queue: null,//需要更新的update
next: null,//下一个hook
};
```
下面来看下memoizedState对应的值
- useState:例如`const [state, updateState] = useState(initialState)`,`memoizedState等于`state的值
- useReducer:例如`const [state, dispatch] = useReducer(reducer, {});`,`memoizedState等于`state的值
- useEffect:在mountEffect时会调用pushEffect创建effect链表,`memoizedState`就等于effect链表,effect链表也会挂载到fiber.updateQueue上,每个effect上存在useEffect的第一个参数回调和第二个参数依赖数组,例如,`useEffect(callback, [dep])`,effect就是{create:callback, dep:dep,...}
- useRef:例如`useRef(0)`,memoizedState`就等于`{current: 0}
- useMemo:例如`useMemo(callback, [dep])`,`memoizedState`等于`[callback(), dep]`
- useCallback:例如`useCallback(callback, [dep])`,`memoizedState`等于`[callback, dep]`。`useCallback`保存`callback`函数,`useMemo`保存`callback`的执行结果
#### useState&useReducer
之所以把useState和useReducer放在一起,是因为在源码中useState就是有默认reducer参数的useReducer。
- useState&useReducer声明
resolveDispatcher函数会获取当前的Dispatcher
```js
function useState(initialState) {
var dispatcher = resolveDispatcher();
return dispatcher.useState(initialState);
}
function useReducer(reducer, initialArg, init) {
var dispatcher = resolveDispatcher();
return dispatcher.useReducer(reducer, initialArg, init);
}
```
- mount阶段
mount阶段useState调用mountState,useReducer调用mountReducer,唯一区别就是它们创建的queue中lastRenderedReducer不一样,mount有初始值basicStateReducer,所以说useState就是有默认reducer参数的useReducer。
```js
function mountState(//
initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction>] {
const hook = mountWorkInProgressHook();//创建当前hook
if (typeof initialState === 'function') {
initialState = initialState();
}
hook.memoizedState = hook.baseState = initialState;//hook.memoizedState赋值
const queue = (hook.queue = {//赋值hook.queue
pending: null,
dispatch: null,
lastRenderedReducer: basicStateReducer,//和mountReducer的区别
lastRenderedState: (initialState: any),
});
const dispatch: Dispatch,
> = (queue.dispatch = (dispatchAction.bind(
null,
currentlyRenderingFiber,
queue,
): any));
return [hook.memoizedState, dispatch];//返回memoizedState和dispatch
}
function mountReducer<S, I, A>(
reducer: (S, A) => S,
initialArg: I,
init?: I => S,
): [S, Dispatch] {
const hook = mountWorkInProgressHook();//创建当前hook
let initialState;
if (init !== undefined) {
initialState = init(initialArg);
} else {
initialState = ((initialArg: any): S);
}
hook.memoizedState = hook.baseState = initialState;//hook.memoizedState赋值
const queue = (hook.queue = {//创建queue
pending: null,
dispatch: null,
lastRenderedReducer: reducer,
lastRenderedState: (initialState: any),
});
const dispatch: Dispatch = (queue.dispatch = (dispatchAction.bind(//创建dispatch函数
null,
currentlyRenderingFiber,
queue,
): any));
return [hook.memoizedState, dispatch];//返回memoizedState和dispatch
}
```
```react
function basicStateReducer(state: S, action: BasicStateAction): S {
return typeof action === 'function' ? action(state) : action;
}
```
- update阶段
update时会根据hook中的update计算新的state
```react
function updateReducer<S, I, A>(
reducer: (S, A) => S,
initialArg: I,
init?: I => S,
): [S, Dispatch] {
const hook = updateWorkInProgressHook();//获取hook
const queue = hook.queue;
queue.lastRenderedReducer = reducer;
//...更新state和第12章的state计算逻辑基本一致
const dispatch: Dispatch = (queue.dispatch: any);
return [hook.memoizedState, dispatch];
}
```
- 执行阶段
useState执行setState后会调用dispatchAction,dispatchAction做的事情就是讲Update加入queue.pending中,然后开始调度
```react
function dispatchAction(fiber, queue, action) {
var update = {//创建update
eventTime: eventTime,
lane: lane,
suspenseConfig: suspenseConfig,
action: action,
eagerReducer: null,
eagerState: null,
next: null
};
//queue.pending中加入update
var alternate = fiber.alternate;
if (fiber === currentlyRenderingFiber$1 || alternate !== null && alternate === currentlyRenderingFiber$1) {
//如果是render阶段执行的更新didScheduleRenderPhaseUpdate=true
}
didScheduleRenderPhaseUpdateDuringThisPass = didScheduleRenderPhaseUpdate = true;
} else {
if (fiber.lanes === NoLanes && (alternate === null || alternate.lanes === NoLanes)) {
//如果fiber不存在优先级并且当前alternate不存在或者没有优先级,那就不需要更新了
//优化的步骤
}
scheduleUpdateOnFiber(fiber, lane, eventTime);
}
}
```
#### useEffect
- 声明
获取并返回useEffect函数
```js
export function useEffect(
create: () => (() => void) | void,
deps: Array | void | null,
): void {
const dispatcher = resolveDispatcher();
return dispatcher.useEffect(create, deps);
}
```
- mount阶段
调用mountEffect,mountEffect调用mountEffectImpl,hook.memoizedState赋值为effect链表
```jsx
function mountEffectImpl(fiberFlags, hookFlags, create, deps): void {
const hook = mountWorkInProgressHook();//获取hook
const nextDeps = deps === undefined ? null : deps;//依赖
currentlyRenderingFiber.flags |= fiberFlags;//增加flag
hook.memoizedState = pushEffect(//memoizedState=effects环状链表
HookHasEffect | hookFlags,
create,
undefined,
nextDeps,
);
}
```
- update阶段
浅比较依赖,如果依赖性变了pushEffect第一个参数传HookHasEffect | hookFlags,HookHasEffect表示useEffect依赖项改变了,需要在commit阶段重新执行
```jsx
function updateEffectImpl(fiberFlags, hookFlags, create, deps): void {
const hook = updateWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
let destroy = undefined;
if (currentHook !== null) {
const prevEffect = currentHook.memoizedState;
destroy = prevEffect.destroy;//
if (nextDeps !== null) {
const prevDeps = prevEffect.deps;
if (areHookInputsEqual(nextDeps, prevDeps)) {//比较deps
//即使依赖相等也要将effect加入链表,以保证顺序一致
pushEffect(hookFlags, create, destroy, nextDeps);
return;
}
}
}
currentlyRenderingFiber.flags |= fiberFlags;
hook.memoizedState = pushEffect(
//参数传HookHasEffect | hookFlags,包含hookFlags的useEffect会在commit阶段执行这个effect
HookHasEffect | hookFlags,
create,
destroy,
nextDeps,
);
}
```
- 执行阶段
在第9章commit阶段的commitLayoutEffects函数中会调用schedulePassiveEffects,将useEffect的销毁和回调函数push到pendingPassiveHookEffectsUnmount和pendingPassiveHookEffectsMount中,然后在mutation之后调用flushPassiveEffects依次执行上次render的销毁函数回调和本次render 的回调函数
```jsx
const unmountEffects = pendingPassiveHookEffectsUnmount;
pendingPassiveHookEffectsUnmount = [];
for (let i = 0; i < unmountEffects.length; i += 2) {
const effect = ((unmountEffects[i]: any): HookEffect);
const fiber = ((unmountEffects[i + 1]: any): Fiber);
const destroy = effect.destroy;
effect.destroy = undefined;
if (typeof destroy === 'function') {
try {
destroy();//销毁函数执行
} catch (error) {
captureCommitPhaseError(fiber, error);
}
}
}
const mountEffects = pendingPassiveHookEffectsMount;
pendingPassiveHookEffectsMount = [];
for (let i = 0; i < mountEffects.length; i += 2) {
const effect = ((mountEffects[i]: any): HookEffect);
const fiber = ((mountEffects[i + 1]: any): Fiber);
try {
const create = effect.create;//本次render的创建函数
effect.destroy = create();
} catch (error) {
captureCommitPhaseError(fiber, error);
}
}
```
#### useRef
sring类型的ref已经不在推荐使用(源码中string会生成refs,发生在coerceRef函数中),ForwardRef只是把ref通过传参传下去,createRef也是{current: any这种结构,所以我们只讨论function或者{current: any}的useRef
```js
//createRef返回{current: any}
export function createRef(): RefObject {
const refObject = {
current: null,
};
return refObject;
}
```
- 声明阶段
和其他hook一样
```js
export function useRef(initialValue: T): {|current: T|} {
const dispatcher = resolveDispatcher();
return dispatcher.useRef(initialValue);
}
```
- mount阶段
mount时会调用mountRef,创建hook和ref对象。
```js
function mountRef(initialValue: T): {|current: T|} {
const hook = mountWorkInProgressHook();//获取useRef
const ref = {current: initialValue};//ref初始化
hook.memoizedState = ref;
return ref;
}
```
render阶段:将带有ref属性的Fiber标记上Ref Tag,这一步发生在beginWork和completeWork函数中的markRef
```js
export const Ref = /* */ 0b0000000010000000;
```
```js
//beginWork中
function markRef(current: Fiber | null, workInProgress: Fiber) {
const ref = workInProgress.ref;
if (
(current === null && ref !== null) ||
(current !== null && current.ref !== ref)
) {
workInProgress.effectTag |= Ref;
}
}
//completeWork中
function markRef(workInProgress: Fiber) {
workInProgress.effectTag |= Ref;
}
```
commit阶段:
会在commitMutationEffects函数中判断ref是否改变,如果改变了会先执行commitDetachRef先删除之前的ref,然后在commitLayoutEffect中会执行commitAttachRef赋值ref。
```js
function commitMutationEffects(root: FiberRoot, renderPriorityLevel) {
while (nextEffect !== null) {
const effectTag = nextEffect.effectTag;
// ...
if (effectTag & Ref) {
const current = nextEffect.alternate;
if (current !== null) {
commitDetachRef(current);//移除ref
}
}
}
```
```js
function commitDetachRef(current: Fiber) {
const currentRef = current.ref;
if (currentRef !== null) {
if (typeof currentRef === 'function') {
currentRef(null);//类型是function,则调用
} else {
currentRef.current = null;//否则赋值{current: null}
}
}
}
```
```js
function commitAttachRef(finishedWork: Fiber) {
const ref = finishedWork.ref;
if (ref !== null) {
const instance = finishedWork.stateNode;//获取ref的实例
let instanceToUse;
switch (finishedWork.tag) {
case HostComponent:
instanceToUse = getPublicInstance(instance);
break;
default:
instanceToUse = instance;
}
if (typeof ref === 'function') {//ref赋值
ref(instanceToUse);
} else {
ref.current = instanceToUse;
}
}
}
```
- update阶段
update时调用updateRef获取获取当前useRef,然后返回hook链表
```js
function updateRef(initialValue: T): {|current: T|} {
const hook = updateWorkInProgressHook();//获取当前useRef
return hook.memoizedState;//返回hook链表
}
```
#### useMemo&useCallback
- 声明阶段
和其他hook 一样
- mount阶段
mount阶段useMemo和useCallback唯一区别是在memoizedState中存贮callback还是callback计算出来的函数
```js
function mountMemo(
nextCreate: () => T,
deps: Array | void | null,
): T {
const hook = mountWorkInProgressHook();//创建hook
const nextDeps = deps === undefined ? null : deps;
const nextValue = nextCreate();//计算value
hook.memoizedState = [nextValue, nextDeps];//把value和依赖保存在memoizedState中
return nextValue;
}
function mountCallback(callback: T, deps: Array | void | null): T {
const hook = mountWorkInProgressHook();//创建hook
const nextDeps = deps === undefined ? null : deps;
hook.memoizedState = [callback, nextDeps];//把callback和依赖保存在memoizedState中
return callback;
}
```
- update阶段
update时也一样,唯一区别就是直接用回调函数还是执行回调后返回的value作为[?, nextDeps]赋值给memoizedState
```js
function updateMemo(
nextCreate: () => T,
deps: Array | void | null,
): T {
const hook = updateWorkInProgressHook();//获取hook
const nextDeps = deps === undefined ? null : deps;
const prevState = hook.memoizedState;
if (prevState !== null) {
if (nextDeps !== null) {
const prevDeps: Array | null = prevState[1];
if (areHookInputsEqual(nextDeps, prevDeps)) {//浅比较依赖
return prevState[0];//没变 返回之前的状态
}
}
}
const nextValue = nextCreate();//有变化重新调用callback
hook.memoizedState = [nextValue, nextDeps];
return nextValue;
}
function updateCallback(callback: T, deps: Array | void | null): T {
const hook = updateWorkInProgressHook();//获取hook
const nextDeps = deps === undefined ? null : deps;
const prevState = hook.memoizedState;
if (prevState !== null) {
if (nextDeps !== null) {
const prevDeps: Array | null = prevState[1];
if (areHookInputsEqual(nextDeps, prevDeps)) {//浅比较依赖
return prevState[0];//没变 返回之前的状态
}
}
}
hook.memoizedState = [callback, nextDeps];//变了重新将[callback, nextDeps]赋值给memoizedState
return callback;
}
```
#### useLayoutEffect
useLayoutEffect和useEffect一样,只是调用的时机不同,它是在commit阶段的commitLayout函数中同步执行
#### forwardRef
forwardRef也非常简单,就是传递ref属性
```js
export function forwardRef<Props, ElementType: React$ElementType>(
render: (props: Props, ref: React$Ref) => React$Node,
) {
const elementType = {
$$typeof: REACT_FORWARD_REF_TYPE,
render,
};
return elementType;
}
//ForwardRef第二个参数是ref对象
let children = Component(props, secondArg);
```