在上一篇博客,初步使用了 useReducer, useState; 现在接着实现useEffect, useLayoutEffect。
-
useEffect和useLayoutEffect功能类似,useLayoutEffect是在cimmit 阶段直接执行,useEffect则是在cimmit阶段调用schedulerCallback执行,所以执行顺序是,useLayoutEffect在渲染后立刻执行,useEffect在下一个eventloop的时候执行
实现useEffect和useLayoutEffect
// src\react\hooks.ts
export function renderHooks(wip:any) {
currentlyRenderingFiber = wip as IFiber;
currentlyRenderingFiber.memoizedState = null;
currentlyRenderingFiber.updateQueueOfEffect = [];
currentlyRenderingFiber.updateQueueOfLayout = [];
workInProgressHook = null;
}
export function useEffect(create:Function, deps:Array<any> | null) {
updateEffectIml(HookPassive, create, deps);
}
export function useLayoutEffect(create:Function, deps:Array<any> | null) {
updateEffectIml(HookLayout, create, deps);
}
function updateEffectIml(hookFlags:number, create:Function, deps:Array<any> | null) {
const hook = updateWorkInProgressHook();
if (!hook.memoizedState) {
// 第一次渲染
hook.memoizedState = { create, deps, HookLayout };
} else {
if (areHookInputsEqual(hook.memoizedState.deps, deps)) {
return;
}
hook.memoizedState = { create, deps, HookLayout };
}
if (hookFlags & HookLayout) {
currentlyRenderingFiber?.updateQueueOfLayout.push(hook.memoizedState);
} else if (hookFlags & HookPassive) {
currentlyRenderingFiber?.updateQueueOfEffect.push(hook.memoizedState);
}
}
- 逻辑是每次渲染在
fiber上开辟一个队列updateQueueOfxxx
- 然后在hook链表上拿到memoizedState,里面存放着
create,deps,flags
- create是deps发生变化时执行的函数
- deps 是依赖项
- flags 用来标记
useEffect和 useLayoutEffect
- 渲染时,对比
memoizedState.deps和传入的deps是否相等,然后把deps发生变化的hook放入updateQueueOfxxx
修改commit 阶段调用updateQueueOfxxx里面的create
// src\react\ReactFiberWorkLoop.ts
function commitWorker(wip:any) {
// ...
const { stateNode, flags, type } = wip;
if (isFunction(type)) {
invokesHooks(wip);
}
// ...
}
function invokesHooks(wip:any) {
const { updateQueueOfEffect, updateQueueOfLayout } = wip;
if (updateQueueOfEffect && updateQueueOfEffect.length) {
updateQueueOfEffect.forEach(({ create }:any) => schedulerCallback(() => {
create();
return true;
}));
}
if (updateQueueOfLayout && updateQueueOfLayout.length) {
updateQueueOfLayout.forEach(({ create }:any) => create());
}
}
-
useLayoutEffect 直接执行
-
useEffect 放入schedulerCallback,下一次执行
源码
网友评论