这里写自定义目录标题
1、寻找hooks相关代码
index.js
文件路径:react-main/packages/react/index.js
/在这个文件我们能看到我们能 import {xxx} from 'react’的所有方法,它是react的入口文件
export{
Component,
PureComponent,
...,
useEffect,
useImperativeHandle,
useInsertionEffect,
useLayoutEffect,
useMemo,
...,
}
ReactHooks.js
我们已经提前知道了create 和 deps 两个参数的作用,所以这个方法看起来并没有什么难懂的
调用了resolveDispatcher()返回的实例的useEffect方法
export function useEffect(
create: () => (() => void) | void,
deps: Array<mixed> | void | null,
): void {
const dispatcher = resolveDispatcher();
return dispatcher.useEffect(create, deps);
}
返回了ReactCurrentDispatcher.current
function resolveDispatcher() {
const dispatcher = ReactCurrentDispatcher.current;
if (__DEV__) {
if (dispatcher === null) {
console.error('这里的error被我删了');
}
}
return ((dispatcher: any): Dispatcher);
}
然鹅这里并没简化,并且线索断了,只知道这个Dispatcher.current的type类型
但是建立在我们对react的了解上知道它与Fiber紧密关联,以及发现这个文件是在ReactFiber里的,我们有理由相信它在ReactFiberHooks相关文件里,我们去找找看
我们应该试图找到一行给ReactCurrentDispatcher.current赋值的代码
import type {Dispatcher} from 'react-reconciler/src/ReactInternalTypes';
export const ReactCurrentDispatcher = {
current: (null: null | Dispatcher),
};
// 平时写代码的对照
useEffect(() => {
console.log('do actions')
// return () => UnmountAction
}, [userId, userName]);
ReactFiberHooks.new.js
果然让我们找到了
renderWithHooks这个方法名其实很好懂,大概就是渲染的时候调用的
current我们在这里脑补成Fiber的一个实例对象,那么这个赋值就很好解释了
如果还没有创建Fiber实例,那就铁走挂载方法,或者它某个状态被置为null时,也是走挂载方法,否则的话就执行更新
export function renderWithHooks<Props, SecondArg>(
current: Fiber | null,
workInProgress: Fiber,
Component: (p: Props, arg: SecondArg) => any,
props: Props,
secondArg: SecondArg,
nextRenderLanes: Lanes,
): any {
if (__DEV__) {
// 开发环境的代码直接干掉
} else {
// 当前将要触发的(行为)? memoizedState存储的effect
// 下文会提到
ReactCurrentDispatcher.current =
current === null || current.memoizedState === null
? HooksDispatcherOnMount
: HooksDispatcherOnUpdate;
}
}
总结
搁这绕了半天其实就这
const HooksDispatcherOnMount: Dispatcher = {
...,
useEffect: mountEffect,
}
const HooksDispatcherOnUpdate: Dispatcher = {
...,
useEffect: updateEffect,
}
export function useEffect(
create: () => (() => void) | void,
deps: Array<mixed> | void | null,
): void {
if(Fiber === null || Fiber.memoizedState === null) {
// HooksDispatcherOnMount.useEffect
return mountEffect(create, deps)
} else{
// HooksDispatcherOnUpdate.useEffect
return updateEffect(create, deps)
}
}
2、代码解读
mountEffect、updateEffect
PassiveEffect、PassiveStaticEffect、HookPassive 这三个参数是常量,一堆奇奇怪怪的数字,应该是与运算有关跟逻辑没啥关系,这里我们先不管
function mountEffect(
create: () => (() => void) | void,
deps: Array<mixed> | void | null,
): void {
if (__DEV__ && xxxxxxxxxxxx) {
// 跟dev环境有关代码直接干掉
} else {
return mountEffectImpl(
PassiveEffect | PassiveStaticEffect,
HookPassive,
create,
deps,
);
}
}
function updateEffect(
create: () => (() => void) | void,
deps: Array<mixed> | void | null,
): void {
return updateEffectImpl(PassiveEffect, HookPassive, create, deps);
}
mountEffectImpl
function mountEffectImpl(fiberFlags, hookFlags, create, deps): void {
// hook被赋予了一个类链表结构
const hook = mountWorkInProgressHook();
// deps就是useEffect的第二个数组对象,假如传入了[userId, userName]
const nextDeps = deps === undefined ? null : deps;
// 看样子是传了个数字标识,不用管
currentlyRenderingFiber.flags |= fiberFlags;
hook.memoizedState = pushEffect(
HookHasEffect | hookFlags,
create,
undefined,
nextDeps,
);
}
mountEffectImpl 首先创建了一个hook对象,很明显这返回了一个名字叫做workInProgressHook的链表
初始化链表 或者 append一个初始化的值到尾端
function mountWorkInProgressHook(): Hook {
const hook: Hook = {
memoizedState: null,
baseState: null,
baseQueue: null,
queue: null,
next: null,
};
if (workInProgressHook === null) {
// 这里的memoizedState 和 上边那个不一样
// 这是顾名思义 是当前正在渲染的fiber
// 这里很有意思的是,对象是按址索引的,那么下面更改workInProgressHook,currentlyRenderingFiber.memoizedState 是否会同步更改呢?
currentlyRenderingFiber.memoizedState = workInProgressHook = hook;
} else {
workInProgressHook = workInProgressHook.next = hook;
}
return workInProgressHook;
}
经过了以上的操作,这时候当前的workInProgressHook 或者说 currentlyRenderingFiber.memoizedState大概长这样,然后去看看pushEffect干了啥
到这里我们可以大胆猜测一下,所有的Effects都按照顺序都存储在workInProgressHook
currentlyRenderingFiber 是一个Fiber类型
type Fiber {
memoizedState: any,
// A queue of state updates and callbacks.
updateQueue: any
}
{
memoizedState: pushEffect(常量, () => console.log('do actions'), undefined, [userId, userName]),
baseState,
baseQueue,
queue,
next: {
memoizedState: null,
baseState: null,
baseQueue: null,
queue: null,
next: null
}
}
pushEffect
这神tm又是个链表
function pushEffect(tag, create, destroy, deps) {
const effect: Effect = {
tag,
create,
destroy,
deps,
// Circular
// 这里的注释是循环?意思是这个链表尾部指向头部么
next: (null: any),
};
// 从当前正在渲染的fiber列表中取出组件更新队列
let componentUpdateQueue: null | FunctionComponentUpdateQueue = (currentlyRenderingFiber.updateQueue: any);
// 如果队列为空,就创建一个队列
if (componentUpdateQueue === null) {
componentUpdateQueue = createFunctionComponentUpdateQueue();
currentlyRenderingFiber.updateQueue = (componentUpdateQueue: any);
// 上边说了要形成闭环,并且这是初始化,所以effect.next 指向自己,然后赋给lastEffect
componentUpdateQueue.lastEffect = effect.next = effect;
} else {
// 如果不是空
const lastEffect = componentUpdateQueue.lastEffect;
if (lastEffect === null) {
componentUpdateQueue.lastEffect = effect.next = effect;
} else {
const firstEffect = lastEffect.next;
lastEffect.next = effect;
effect.next = firstEffect;
componentUpdateQueue.lastEffect = effect;
}
}
return effect;
}
// 如果为空的情况下,componentUpdateQueue长这样
{
lastEffect: {
tag: 常量,
create : () => console.log('do actions'),
destroy: undefined,
deps: [userId, userName],
next: 下一个还是自己
},
stores: null,
};
// 如果不为空,
{
lastEffect: {
tag: 常量,
create : () => console.log('do actions'),
destroy: undefined,
deps: [userId, userName],
next: {
tag: 常量,
create : () => console.log('do actions 2'),
destroy: undefined,
deps: [userId2, userName2],
next: 指向链表头部
}
},
stores: null,
}
updateEffectImpl
// Represents the phase in which the effect (not the clean-up) fires.
export const Insertion = /* */ 0b0010;
export const Layout = /* */ 0b0100;
export const Passive = /* */ 0b1000;
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;
// 销毁的function
destroy = prevEffect.destroy;
if (nextDeps !== null) {
const prevDeps = prevEffect.deps;
if (areHookInputsEqual(nextDeps, prevDeps)) {
// 如果相同的话,hookFlags = Passive
hook.memoizedState = pushEffect(hookFlags, create, destroy, nextDeps);
return;
}
}
}
currentlyRenderingFiber.flags |= fiberFlags;
hook.memoizedState = pushEffect(
HookHasEffect | hookFlags,
create,
destroy,
nextDeps,
);
}
updateWorkInProgressHook
currentlyRenderingFiber.alternate,在Fiber接口中是着么定义的
This is a pooled version of a Fiber. Every fiber that gets updated will eventually have a pair. There are cases when we can clean up pairs to save memory if we need to
这个函数看起来麻烦,其实就是把hook的那个双链表结构给搞过来
function updateWorkInProgressHook(): Hook {
// This function is used both for updates and for re-renders triggered by a
// render phase update. It assumes there is either a current hook we can
// clone, or a work-in-progress hook from a previous render pass that we can
// use as a base. When we reach the end of the base list, we must switch to
// the dispatcher used for mounts.
let nextCurrentHook: null | Hook;
if (currentHook === null) {
// 其实就是在更新的时候,拿了上一轮(Mount)时候生成的 memoizedState 副本
// currentHook = nextCurrentHook;
// 便于 updateWorkInProgressHook 后的比对
const current = currentlyRenderingFiber.alternate;
if (current !== null) {
nextCurrentHook = current.memoizedState;
} else {
nextCurrentHook = null;
}
} else {
nextCurrentHook = currentHook.next;
}
let nextWorkInProgressHook: null | Hook;
if (workInProgressHook === null) {
nextWorkInProgressHook = currentlyRenderingFiber.memoizedState;
} else {
nextWorkInProgressHook = workInProgressHook.next;
}
if (nextWorkInProgressHook !== null) {
// There's already a work-in-progress. Reuse it.
workInProgressHook = nextWorkInProgressHook;
nextWorkInProgressHook = workInProgressHook.next;
currentHook = nextCurrentHook;
} else {
// Clone from the current hook.
if (nextCurrentHook === null) {
throw new Error('Rendered more hooks than during the previous render.');
}
currentHook = nextCurrentHook;
const newHook: Hook = {
memoizedState: currentHook.memoizedState,
baseState: currentHook.baseState,
baseQueue: currentHook.baseQueue,
queue: currentHook.queue,
next: null,
};
if (workInProgressHook === null) {
// This is the first hook in the list.
currentlyRenderingFiber.memoizedState = workInProgressHook = newHook;
} else {
// Append to the end of the list.
workInProgressHook = workInProgressHook.next = newHook;
}
}
return workInProgressHook;
}
commitHookEffectListMount
ReactFiberCommitWork中可以找到此方法
都是在Fiber的最后,依据以上构造的数据结构来处理
function commitHookEffectListUnmount() {
const updateQueue: FunctionComponentUpdateQueue | null = (finishedWork.updateQueue: any);
const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
if (lastEffect !== null) {
// 猜测
// 所以说 effect 链表是一个环,lastEffect是最后一个,lastEffect.next是第一个
// 而 currentlyRenderingFiber.memoizedState 可以直接取到,所以不必是环
// 至此 在一个循环中,这个闭包双链表结构结束了他的使命
const firstEffect = lastEffect.next;
let effect = firstEffect;
do {
// create 就是useEffect传入的第一个参数,他可能有返回值,给了destroy
const create = effect.create;
effect.destroy = create();
effect = effect.next;
}while (effect !== firstEffect)
}
}