美文网首页
React深入8-源码4 (useEffect,useLayou

React深入8-源码4 (useEffect,useLayou

作者: 申_9a33 | 来源:发表于2022-03-10 14:53 被阅读0次

上一篇博客,初步使用了 useReducer, useState; 现在接着实现useEffect, useLayoutEffect

  • useEffectuseLayoutEffect功能类似,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 用来标记 useEffectuseLayoutEffect
  • 渲染时,对比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,下一次执行

源码

相关文章

网友评论

      本文标题:React深入8-源码4 (useEffect,useLayou

      本文链接:https://www.haomeiwen.com/subject/ejifdrtx.html