IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> hooks-useReducer源码 -> 正文阅读

[JavaScript知识库]hooks-useReducer源码

hooks-useReducer源码

现目前react端都流行hooks写法,但不能只会用而不清楚它是怎么实现的,所以今天我就来研究hooks到底是怎么实现的。源码版本:17.0.2

demo:

function FunctionComponent(props) {
  // 两个reducer
  const [state, dispatch] = useReducer((x)=>x+1, 1);
  const [stateTwo, dispatchTwo] = useReducer((x)=>x+1, 3);

  return (
    <div className="border">
      <p onClick={()=>{
        dispatch();
        dispatchTwo();
      }}>{props.name}{state}{stateTwo}</p>
    </div>
  );
}

useReducer流程

在这里插入图片描述

renderWithHooks

这个函数的主要作用就是执行我们的useReducer、useState等hooks,进行hooks的初始化工作。包括fiber,

function renderWithHooks(current, workInProgress, Component, props, secondArg, nextRenderLanes) {
  renderLanes = nextRenderLanes;
    // 当前正在渲染的fiber为workInProgress
  currentlyRenderingFiber$1 = workInProgress;
// 初始化当前正在工作的fiber的state值为null
  workInProgress.memoizedState = null;
  workInProgress.updateQueue = null;
  workInProgress.lanes = NoLanes; 
    // 
      ReactCurrentDispatcher$1.current = HooksDispatcherOnMountInDEV;
       // 执行函数组件,会执行到我们的useReducer,进行reducer的初始化
  var children = Component(props, secondArg); 

  if (didScheduleRenderPhaseUpdateDuringThisPass) {
    var numberOfReRenders = 0;

    do {
      currentHook = null;
      workInProgressHook = null;
      workInProgress.updateQueue = null;


       if (current !== null && current.memoizedState !== null) {
      ReactCurrentDispatcher$1.current = HooksDispatcherOnUpdateInDEV;
    } else if (hookTypesDev !== null) {
      
      ReactCurrentDispatcher$1.current = HooksDispatcherOnMountWithHookTypesInDEV;
    } else {
        // mount 会进入到这里
      ReactCurrentDispatcher$1.current = HooksDispatcherOnMountInDEV;
    }
        // 执行函数组件,会执行到我们的useReducer,进行reducer的初始化
      children = Component(props, secondArg);
    } while (didScheduleRenderPhaseUpdateDuringThisPass);
  } 

  return children;
}

hooks-useReducer

 const [state, dispatch] = useReducer((x)=>x+1, 1);// 他是怎么初始化的。
和hooks相关的全局变量。
currentlyRenderingFiber$1 // 当前渲染的fiber
currentHook;// 当前hooks
workInProgressHook;// 当前在工作树的hooks
ReactCurrentDispatcher$1// 当前hooks的pispatch
  • useReducer,不管是mount还是update都是这样的结构,只不过mount是mountReducer,update是updateReducer,
  • mount会将我们的hooks一个一个的以链表的形式绑定到fiber的memoizedState
// mount
useReducer: function (reducer, initialArg, init) {
    // 当前hooks的名字
      currentHookNameInDev = 'useReducer';
    // 将所有的hooks标记起来。有其他作用。对主流程没什么影响。
      mountHookTypesDev();
    // 
      var prevDispatcher = ReactCurrentDispatcher$1.current;
      ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV;

      try {
        return mountReducer(reducer, initialArg, init);
      } finally {
        ReactCurrentDispatcher$1.current = prevDispatcher;
      }
    },
 //update    
useReducer: function (reducer, initialArg, init) {
      currentHookNameInDev = 'useReducer';
      updateHookTypesDev();
      var prevDispatcher = ReactCurrentDispatcher$1.current;
      ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV;

      try {
        return updateReducer(reducer, initialArg, init);
      } finally {
        ReactCurrentDispatcher$1.current = prevDispatcher;
      }
    },

mountReducer

// hooks的初始化
function mountWorkInProgressHook() {
    // 每一个hooks进来都具备这些属性
  var hook = {
    memoizedState: null,// 组件自己的状态
    baseState: null,
    baseQueue: null,
    queue: null,
    next: null// 下一个hooks
  };
	// 当前工作在fiber当前工作的hooks没有的话
  if (workInProgressHook === null) {
	// 当前渲染的fiber的memoizedState就是刚刚定义的hook
    currentlyRenderingFiber$1.memoizedState = workInProgressHook = hook;
  } else {
    // 第二哥hook之后,将workInProgressHook为当前的,前一个的next指向当前
    workInProgressHook = workInProgressHook.next = hook;
  }

  return workInProgressHook;
}
// useReducer 初始化执行到这里
function mountReducer(reducer, initialArg, init) {
    // hooks的初始化
  var hook = mountWorkInProgressHook();
    // 上面的hook会形成一个链表 workInProgressHook 指向最后一个hook,在本例中是第二个。
  var initialState;
	// useReducer的初始值
  if (init !== undefined) {
    initialState = init(initialArg);
  } else {
    initialState = initialArg;
  }
// 赋值给fiber的memoizedState为初始值
  hook.memoizedState = hook.baseState = initialState;
    // 将我们写好的reducer和初始值挂到队列,后续在update阶段会使用到
  var queue = hook.queue = {
    pending: null,
    dispatch: null,
    lastRenderedReducer: reducer,
    lastRenderedState: initialState
  };
    // 触发函数 bind绑定的函数,需要触发才会执行,会有当前在currentlyRenderingFiber$1,和队列
  var dispatch = queue.dispatch = dispatchAction.bind(null, currentlyRenderingFiber$1, queue);
  return [hook.memoizedState, dispatch];
}

dispatchAction

当我们在react组件中去调用,这个函数的时候。我们就进入了更新阶段

function dispatchAction(fiber, queue, action) {
  var eventTime = requestEventTime();
  var lane = requestUpdateLane(fiber);
  var update = {
    lane: lane,
    action: action,
    eagerReducer: null,
    eagerState: null,
    next: null
  }; // 
	// pending是当前quene的pending。
  var pending = queue.pending;

  if (pending === null) {
    // 首次
    update.next = update;
  } else {
      
    update.next = pending.next;
    pending.next = update;
  }

  queue.pending = update;
    // 老节点
  var alternate = fiber.alternate;

  if (fiber === currentlyRenderingFiber$1 || alternate !== null && alternate === currentlyRenderingFiber$1) {
    didScheduleRenderPhaseUpdateDuringThisPass = didScheduleRenderPhaseUpdate = true;
  } else {
    if (fiber.lanes === NoLanes && (alternate === null || alternate.lanes === NoLanes)) {
   		// 可以看这里的逻辑,前面的 是其他情况的处理
        // lastRenderedReducer 是我们的reducer
      var lastRenderedReducer = queue.lastRenderedReducer;

      if (lastRenderedReducer !== null) {
        var prevDispatcher;

        {
            // 
          prevDispatcher = ReactCurrentDispatcher$1.current;
            // 这里将我们的dispatch更改为更新的useReducer
          ReactCurrentDispatcher$1.current =InvalidNestedHooksDispatcherOnUpdateInDEV;
        }

        try {
          var currentState = queue.lastRenderedState;
          var eagerState = lastRenderedReducer(currentState, action); // Stash the 
          update.eagerReducer = lastRenderedReducer;
          update.eagerState = eagerState;

        } catch (error) {
        } finally {
          {
            ReactCurrentDispatcher$1.current = prevDispatcher;
          }
        }
      }
    }
     // 进入react的调度更新,会重新进入renderWithHooks  
      scheduleUpdateOnFiber(fiber, lane, eventTime);
  }
}

updateReducer

依靠的是我们在初始化时绑定的currentlyRenderingFiber$1上面的memoizedState,来更改当前hooks的,workInProgressHook和currentHook

//
function updateWorkInProgressHook() {
  var nextCurrentHook;
	// 查找是否有下一个currentHooks
  if (currentHook === null) {
      // 首次 还没有当前hooks
    var current = currentlyRenderingFiber$1.alternate;
	// 有老的fiber节点,没有就为null
    if (current !== null) {
      nextCurrentHook = current.memoizedState;
    } else {
      nextCurrentHook = null;
    }
  } else {
    nextCurrentHook = currentHook.next;
  }

  var nextWorkInProgressHook;
// 	查找下一个nextWorkInProgressHook
    // 第一次更新必定没有工作中的workInProgressHook
  if (workInProgressHook === null) {
    nextWorkInProgressHook = currentlyRenderingFiber$1.memoizedState;
  } else {
    nextWorkInProgressHook = workInProgressHook.next;
  }

  if (nextWorkInProgressHook !== null) {
    workInProgressHook = nextWorkInProgressHook;
    nextWorkInProgressHook = workInProgressHook.next;
    currentHook = nextCurrentHook;
  } else {
  // 	currentHook置为下一个

    currentHook = nextCurrentHook;
      //新的hooks
    var newHook = {
      memoizedState: currentHook.memoizedState,
      baseState: currentHook.baseState,
      baseQueue: currentHook.baseQueue,
      queue: currentHook.queue,
      next: null
    };

    if (workInProgressHook === null) {
      currentlyRenderingFiber$1.memoizedState = workInProgressHook = newHook;
    } else {

      workInProgressHook = workInProgressHook.next = newHook;
    }
  }

  return workInProgressHook;
}

function updateReducer(reducer, initialArg, init) {
    // 更新当前工作hooks,找到这个reducer的hook
  var hook = updateWorkInProgressHook();
  var queue = hook.queue;// quene装了我们的state和reducer,
    // 我们当前的hooks
  queue.lastRenderedReducer = reducer;
    // 得到的当前hook
  var current = currentHook;

  var baseQueue = current.baseQueue; 

  var pendingQueue = queue.pending;
// 准备update需要的数据
  if (pendingQueue !== null) {
 
    if (baseQueue !== null) {
      var baseFirst = baseQueue.next;
      var pendingFirst = pendingQueue.next;
      baseQueue.next = pendingFirst;
      pendingQueue.next = baseFirst;
    }

    current.baseQueue = baseQueue = pendingQueue;
    queue.pending = null;
  }

  if (baseQueue !== null) {
    var first = baseQueue.next;
    var newState = current.baseState;
    var newBaseState = null;
    var newBaseQueueFirst = null;
    var newBaseQueueLast = null;
    var update = first;

    do {
      var updateLane = update.lane;

      if (!isSubsetOfLanes(renderLanes, updateLane)) {
      
        var clone = {
          lane: updateLane,
          action: update.action,
          eagerReducer: update.eagerReducer,
          eagerState: update.eagerState,
          next: null
        };

        if (newBaseQueueLast === null) {
          newBaseQueueFirst = newBaseQueueLast = clone;
          newBaseState = newState;
        } else {
          newBaseQueueLast = newBaseQueueLast.next = clone;
        } 
        currentlyRenderingFiber$1.lanes = mergeLanes(currentlyRenderingFiber$1.lanes, updateLane);
        markSkippedUpdateLanes(updateLane);
      } else {
        if (newBaseQueueLast !== null) {
          var _clone = {
     
            lane: NoLane,
            action: update.action,
            eagerReducer: update.eagerReducer,
            eagerState: update.eagerState,
            next: null
          };
          newBaseQueueLast = newBaseQueueLast.next = _clone;
        } 


        if (update.eagerReducer === reducer) {
     
          newState = update.eagerState;
        } else {
          var action = update.action;
            // 执行我们传递的reducer得到新的state
          newState = reducer(newState, action);
        }
      }

      update = update.next;
    } while (update !== null && update !== first);

    if (newBaseQueueLast === null) {
      newBaseState = newState;
    } else {
      newBaseQueueLast.next = newBaseQueueFirst;
    } // Mark that the fiber performed work, but only if the new state is
    // different from the current state.


    if (!objectIs(newState, hook.memoizedState)) {
      markWorkInProgressReceivedUpdate();
    }
// 这里结束都是在遍历quene,得到了新的memoizedState,
    hook.memoizedState = newState;
    hook.baseState = newBaseState;
    hook.baseQueue = newBaseQueueLast;
    queue.lastRenderedState = newState;
  }

  var dispatch = queue.dispatch;
    // 从这里返回
  return [hook.memoizedState, dispatch];
}

几句话说明白useReducer

  1. 区分为初始化和更新。初始化,在我们第一次进行调度时,检测到是函数组件,调用renderWithHooks,执行useReducer,调用mountReducer操作,将传入的初始值挂载到fiber的memoizedState,

  2. 更新的时候,dispatch会去触发scheduleUpdateOnFiber,进入调度,再次进到renderWithHooks,执行updateReducer,得到新的state值返回,并重新计算渲染。

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2021-07-10 14:27:32  更:2021-07-10 14:30:03 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/4 21:33:50-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码