对于函数组件而言,整个的调用顺序为 beginWork----> 判断tag--->调用 updateFunctionComponent --->renderWithHooks,所以组件真正的调用发生在renderWithHooks 中,
function renderWithHooks(current, workInProgress, Component, props, secondArg, nextRenderLanes) {
renderLanes = nextRenderLanes; //记录当前正在执行的Fiber节点
currentlyRenderingFiber$1 = workInProgress;
//初始化
workInProgress.memoizedState = null;
workInProgress.updateQueue = null;
workInProgress.lanes = NoLanes; // The following should have already been reset
{ //判断dispatcher 的指向,是执行mount的方法还是update的方法
if (current !== null && current.memoizedState !== null) {
ReactCurrentDispatcher$1.current = HooksDispatcherOnUpdateInDEV;
} else if (hookTypesDev !== null) {
ReactCurrentDispatcher$1.current = HooksDispatcherOnMountWithHookTypesInDEV;
} else {
ReactCurrentDispatcher$1.current = HooksDispatcherOnMountInDEV;
}
} //render 执行构造函数,Component实际上就是 我们的函数组件方法
var children = Component(props, secondArg); // Check if there was a render phase update
//didScheduleRenderPhaseUpdateDuringThisPass 全局变量 判断是否需要 从新render。因为在执行构造函数的时候 只要使用了setstate就会产生 re-render
if (didScheduleRenderPhaseUpdateDuringThisPass) {
//进行 re-render 逻辑
var numberOfReRenders = 0;
do {
didScheduleRenderPhaseUpdateDuringThisPass = false;
localIdCounter = 0;
numberOfReRenders += 1;//记录reRender次数
currentHook = null;
workInProgressHook = null;
workInProgress.updateQueue = null;
ReactCurrentDispatcher$1.current = HooksDispatcherOnRerenderInDEV ;
children = Component(props, secondArg);
} while (didScheduleRenderPhaseUpdateDuringThisPass);
} //执行完后关闭hooks的调用接口
ReactCurrentDispatcher$1.current = ContextOnlyDispatcher;
//....
return children;
}
例如这个组件
function Apple() {
const [num, setNum] = useState(1);
const [name, setName] = useState("");
const [age, setAge] = useState(23);
const handleAdd = () => {
setNum("德玛西亚");
setNum("洛克萨斯");
setAge(age + 1);
};
//这段代码就会引起re-render ,他在组件render的过程中调用了set方法
if (num == 1) {
setNum(num + 1);
}
useEffect(() => {
setName("yangxiang-杨祥");
}, []);
return (
<div className="apple">
<p>
Apple {num},age{age}
</p>
<button onClick={() => handleAdd()}>add Apple</button>
</div>
)
;}
上述renderWithHooks 中的 Component 调用就是 Apple 方法的调用,所以的 hooks调用 也都发生在这Apple函数里,至于之前在renderWithHook中讲到的 didScheduleRenderPhaseUpdateDuringThisPass。产生re-render 的情况,就如上面Apple 组建里面的 if(num==1){setNum(num+1)} 函数组件执行的同时调用 set 方法出发更新 就会产生 re-render , 不用 整棵树从新渲染。
首先在学习hook源码之前先了解一下hook这一块相关的数据结构。
var hook = {
memoizedState: null,
baseState: null,
baseQueue: null,
queue: null,
next: null
};
对于不同的hook 里面所存放的东西各不相同,比如useState 产生的hook,它的memoizedState保存的就是就是你返回的state,比如useEffect,保存的就是一条effect 链表,后面具体细说。
useState/useEffect产生的hook
var hook = {
memoizedState: null, //保存最新的状态
baseState: null, //保存状态
baseQueue: null, //更新中断未更新的update 链表
queue: null, //queue.pending 指向update环状链表的最后一个update
next: null //下一个hook
};
useEffect/useLayoutEffect产生的hook
var hook = {
memoizedState: null, //保存effect 链表
baseState: null,
baseQueue: null,
queue: null,
next: null //下一个hook
};
useRef
var hook = {
memoizedState: null, //{current:XXX}的对象
baseState: null,
baseQueue: null,
queue: null,
next: null //下一个hook
};
useCallback
var hook = {
memoizedState: null, //保存一个数组 [callback,deps],第零位 未 传入的方法,第一位为依赖项
baseState: null,
baseQueue: null,
queue: null,
next: null //下一个hook
};
useMemo
var hook = {
memoizedState: null, //保存一个数组[nextvalue,nextDeps], 一个是值,一个是依赖项
baseState: null,
baseQueue: null,
queue: null,
next: null //下一个hook
};
useState
之前在renderWithHook()方法中曾经提到过,ReactCurrentDispatcher$1.current
所指向的就是我们要调用的hook方法,每一个官方提供的hook 方法在mount 和 update 的不同时期都会调用不同的方法,比如useState 对映有mountState,updateState两个方法,首先这里说明一下这里的mount和update是相对于hook方法的生命周期而言,而之前在讲react的mount和update是指的是应用的生命周期而言。
首先来介绍一下useState,中涉及到的数据结构update,每次调用useState 返回的dispatch函数,也就是 返回数组的第二个,都会生成一个update
var update = {
lane: lane, //优先级
action: action, //可以是一个值,可以是一个函数,比如下方第一个setNum产生的update中action = 1,
//第二个action = ()=> 'yangxiang '
hasEagerState: false,
eagerState: null,
next: null //指向下一个update的指针
};
cosnt [num,setNum] = useState()
cosnt [name,setName] = useState()
我们首先来理一下 hook 数据结构,update 数据节后 ,和fiber 之前的关系
fiber对映了一个工作单元,比如我们的组件,原生dom元素等等,hook 是我们在function Component 中执行官方提供的hook方法,比如执行一个useState()就会产生一个对应的hook,而useState提供了一个我们用于改变状态的set方法比如上面的setNum、setName,每执行一次set方法就会产生一个update.
下面为几个数据结构的关系
fiber----包含---> hook -----包含--->update
mountState
function mountState(initialState) {
//初始化hook
var hook = mountWorkInProgressHook();
if (typeof initialState === 'function') {
initialState = initialState();
}
//保存状态
hook.memoizedState = hook.baseState = initialState;
var queue = {
pending: null,//一个指向由update组成的环转链表最后一个update的指针
interleaved: null,
lanes: NoLanes,
dispatch: null,
lastRenderedReducer: basicStateReducer,
lastRenderedState: initialState
};
hook.queue = queue;
var dispatch = queue.dispatch = dispatchSetState.bind(null, currentlyRenderingFiber$1, queue);
return [hook.memoizedState, dispatch];
}
- 初始化hook
- 处理initialState 为函数的情况
- 保存初始化状态到hook.memoizedState和hook.baseState中
- 生成一个queue ,queue中最重要的作用之一就是通过queue.pending 指向update环转链表的最后一个update
- 生成每一个useState对应的dispatch函数,也就是我们用来改变状态的那个set方法
- 最后返回当前的状态和dispatch方法
我们来看看mountWorkInProgressHook都做了什么
function mountWorkInProgressHook() {
var hook = {
memoizedState: null,
baseState: null,
baseQueue: null, //
queue: null,
next: null
};
if (workInProgressHook === null) {
// This is the first hook in the list
currentlyRenderingFiber$1.memoizedState = workInProgressHook = hook;
} else {
// Append to the end of the list
workInProgressHook = workInProgressHook.next = hook;
}
return workInProgressHook;
}
其实很简单,
- 创建一个hook 数据结构,
- workInProgressHook 即为当前正在执行中的hook,如果 workInProgressHook === null ,则吧创建的hook 赋值给workInProgressHook,并且挂载到当前正在渲染的fiber.memoizedState上,否则就放到链表的最后一个
- 最后返回创建的hook
而另一个需要注意的就是产生用于更新的dispatch函数的方法:dispatchSetState
function dispatchSetState(fiber, queue, action) {
{
if (typeof arguments[3] === 'function') {
error("State updates from the useState() and useReducer() Hooks don't support the " + 'second callback argument. To execute a side effect after ' + 'rendering, declare it in the component body with useEffect().');
}
}
var lane = requestUpdateLane(fiber);
var update = {
lane: lane,
action: action,
hasEagerState: false,
eagerState: null,
next: null
};
if (isRenderPhaseUpdate(fiber)) {
enqueueRenderPhaseUpdate(queue, update);
} else {
enqueueUpdate$1(fiber, queue, update);
var alternate = fiber.alternate;
if (fiber.lanes === NoLanes && (alternate === null || alternate.lanes === NoLanes)) {
var lastRenderedReducer = queue.lastRenderedReducer;
if (lastRenderedReducer !== null) {
var prevDispatcher;
{
prevDispatcher = ReactCurrentDispatcher$1.current;
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
}
try {
var currentState = queue.lastRenderedState;
var eagerState = lastRenderedReducer(currentState, action); // Stash the eagerly computed state, and the reducer used to compute
update.hasEagerState = true;
update.eagerState = eagerState;
if (objectIs(eagerState, currentState)) {
return;
}
} catch (error) {// Suppress the error. It will throw again in the render phase.
} finally {
{
ReactCurrentDispatcher$1.current = prevDispatcher;
}
}
}
}
var eventTime = requestEventTime();
var root = scheduleUpdateOnFiber(fiber, lane, eventTime);
if (root !== null) {
entangleTransitionUpdate(root, queue, lane);
}
}
markUpdateInDevTools(fiber, lane);
}
上述为完整代码实际上的核心代码在这里
function dispatchSetState(fiber, queue, action) {
var update = {
lane: lane,
action: action,
hasEagerState: false,
eagerState: null,
next: null
};
//是否在渲染阶段更新
if (isRenderPhaseUpdate(fiber)) {
enqueueRenderPhaseUpdate(queue, update);
} else {
enqueueUpdate$1(fiber, queue, update);
var alternate = fiber.alternate;
var eventTime = requestEventTime();
var root = scheduleUpdateOnFiber(fiber, lane, eventTime);
}
markUpdateInDevTools(fiber, lane);
}
- 每次调用修改状态的set方法生成一个update数据结构
- 判断是否在渲染阶段更新
- 调用scheduleUpdateOnFiber ,执行调度方法,实现更新
其中主要的代码在enqueueRenderPhaseUpdate()和enqueueUpdate()中,具体来看看做了啥
enqueueRenderPhaseUpdate
function enqueueRenderPhaseUpdate(queue, update) {
didScheduleRenderPhaseUpdateDuringThisPass = didScheduleRenderPhaseUpdate = true;
var pending = queue.pending;
if (pending === null) {
// This is the first update. Create a circular list.
update.next = update;
} else {
update.next = pending.next;
pending.next = update;
}
queue.pending = update;
}
- 进入这个方法说明是在渲染阶段进行了更新, didScheduleRenderPhaseUpdateDuringThisPass = didScheduleRenderPhaseUpdate = true ,触发 renderWithHook 方法里面曾讲到的re-render逻辑
- 判断 hook.queue.pending 是否为空,如果为空则初始化换装链表,不为空则将生成的update放到链表的最后一个
- 把hook.queue.pending 指向最后一个update,对应了之前讲的
enqueueUpdate
function enqueueUpdate$1(fiber, queue, update, lane) {
//是否是多根节点交错更新的情况,非常少见
if (isInterleavedUpdate(fiber)) {
var interleaved = queue.interleaved;
if (interleaved === null) {
// This is the first update. Create a circular list.
update.next = update; // At the end of the current render, this queue's interleaved updates will
// be transferred to the pending queue.
pushInterleavedQueue(queue);
} else {
update.next = interleaved.next;
interleaved.next = update;
}
queue.interleaved = update;
} else {
var pending = queue.pending;
if (pending === null) {
// This is the first update. Create a circular list.
//第一个update,生成环状链表
update.next = update;
} else {
update.next = pending.next;
pending.next = update;
}
queue.pending = update;
}
}
- 以判断是否是多根节点交错更新的情况,非常少见(没实际调试过)
- 核心代码同样是判断pending是否为空,如果等于null则 生成 update的环转链表,否则插入到链表的末尾,最后通过queue.pending 指向最后一个
到此为止mountState部分学习到此为止了
updateState
在hook的update时期调用的就是updateState方法,也就是我们调用对应改变state的dispatch函数的时候,比如 [num,setNum] = useState(1) ,执行setNum(2) 方法时就会执行当前hook方法的update方法
function updateState(initialState) {
return updateReducer(basicStateReducer)
;
}
以上就是updateState全部内容,其实update时期调用的竟然是updateReducer,有点意思,接下来看看updateReducer 究竟做了啥,同时这里的basicStateReducer 又是什么。
function updateReducer(reducer, initialArg, init) {
var hook = updateWorkInProgressHook();
var queue = hook.queue; //前面讲了,hook.queue.pending 指向update环转链表的最后一个update,即链表尾部
if (queue === null) { //mount阶段产生了queue并挂在到了hook上,update阶段 必须有queue否则报错
throw new Error('Should have a queue. This is likely a bug in React. Please file an issue.');
}
queue.lastRenderedReducer = reducer; //这里传的basicStateReducer,后面会讲 var current = currentHook; // The last rebase update that is NOT part of the base state.
//由于之前某些高优先级任务导致更新中断,baseQueue记录的就是尚未处理的最后一个update
var baseQueue = current.baseQueue; // The last pending update that hasn't been processed yet.
var pendingQueue = queue.pending; //当前update链表最后一个update
if (pendingQueue !== null) {
if (baseQueue !== null) { // Merge the pending queue and the base queue.
//合并上述的baseQueue和pendingQueue
var baseFirst = baseQueue.next; //未处理的update环转链表的第一个update
var pendingFirst = pendingQueue.next; //当前产生的update链表的第一个update
baseQueue.next = pendingFirst; //当前产生的更新接入,之前未处理的链表后,baseQueue的尾巴接入pendingQueue的头
pendingQueue.next = baseFirst; //pendingQueue的尾部接入baseQueue的头,最后形成 环
}
{
if (current.baseQueue !== baseQueue) { // Internal invariant that should never happen, but feasibly could in
// the future if we implement resuming, or some form of that.
error('Internal error: Expected work-in-progress queue to be a clone. ' + 'This is a bug in React.');
}
}
current.baseQueue = baseQueue = pendingQueue;
queue.pending = null;
}
if (baseQueue !== null) { // We have a queue to process. 产生的update链表不为空
var first = baseQueue.next; //拿到第一个update
var newState = current.baseState; //useState hook当前的state
var newBaseState = null;
var newBaseQueueFirst = null;
var newBaseQueueLast = null;
var update = first; //刚开始 update 变量 赋值为 链表中第一个更新
//这里的do. while 循环则是在遍历刚刚我们合并的update链表,如果baseQueue === null, 不存在
// 之前的合并操作,整个do{}里面包含的就是 产生心的state的过程
do {
var updateLane = update.lane; //lane优先级
if (!isSubsetOfLanes(renderLanes, updateLane)) {
var clone = {
lane: updateLane,
action: update.action,
hasEagerState: update.hasEagerState,
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, hasEagerState: update.hasEagerState, eagerState: update.eagerState, next: null }; newBaseQueueLast = newBaseQueueLast.next = _clone; } // Process this update. if (update.hasEagerState) { // If this update is a state update (not a reducer) and was processed eagerly, // we can use the eagerly computed state newState = update.eagerState; } else {
//核心代码就在这,
var action = update.action; //取得当前的update的action,可能是函数也可能是具体的值
newState = reducer(newState, action);
}
}
update = update.next; //更新update指针指向下一个更新
} 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();
}
hook.memoizedState = newState;
hook.baseState = newBaseState;
hook.baseQueue = newBaseQueueLast;
queue.lastRenderedState = newState;
}
var lastInterleaved = queue.interleaved;
if (lastInterleaved !== null) {
var interleaved = lastInterleaved;
do {
var interleavedLane = interleaved.lane;
currentlyRenderingFiber$1.lanes = mergeLanes(currentlyRenderingFiber$1.lanes, interleavedLane);
markSkippedUpdateLanes(interleavedLane);
interleaved = interleaved.next;
} while (interleaved !== lastInterleaved);
} else if (baseQueue === null) {
// `queue.lanes` is used for entangling transitions. We can set it back to // zero once the queue is empty.
queue.lanes = NoLanes;
}
var dispatch = queue.dispatch;
return [hook.memoizedState, dispatch];
}
以上为完整代码,看起来有点太多了,精简一下核心常用的代码
function updateReducer(reducer, initialArg, init) {
var hook = updateWorkInProgressHook();//更新当前执行的hook
var queue = hook.queue; //前面讲了,hook.queue.pending 指向update环转链表的最后一个update,即链表尾部
if (queue === null) { //mount阶段产生了queue并挂在到了hook上,update阶段 必须有queue否则报错
throw new Error('Should have a queue. This is likely a bug in React. Please file an issue.');
}
queue.lastRenderedReducer = reducer; //这里传的basicStateReducer,后面会讲
var current = currentHook; // The last rebase update that is NOT part of the base state.
//由于之前某些高优先级任务导致更新中断,baseQueue记录的就是尚未处理的最后一个update
var baseQueue = current.baseQueue; // The last pending update that hasn't been processed yet.
var pendingQueue = queue.pending; //当前update链表最后一个update if (pendingQueue !== null) {
if (baseQueue !== null) {
// Merge the pending queue and the base queue.
//合并上述的baseQueue和pendingQueue
var baseFirst = baseQueue.next;
//未处理的update环转链表的第一个update
var pendingFirst = pendingQueue.next; //当前产生的update链表的第一个update
baseQueue.next = pendingFirst;
//当前产生的更新接入,之前未处理的链表后,baseQueue的尾巴接入pendingQueue的头
pendingQueue.next = baseFirst; //pendingQueue的尾部接入baseQueue的头,最后形成 环
}
current.baseQueue = baseQueue = pendingQueue;
queue.pending = null;
}
if (baseQueue !== null) {
// We have a queue to process. 产生的update链表不为空
var first = baseQueue.next; //拿到第一个update
var newState = current.baseState; //useState hook当前的state
var newBaseState = null;
var newBaseQueueFirst = null;
var newBaseQueueLast = null;
var update = first; //刚开始 update 变量 赋值为 链表中第一个更新
//这里的do. while 循环则是在遍历刚刚我们合并的update链表,如果baseQueue === null, 不存在
// 之前的合并操作,整个do{}里面包含的就是 产生心的state的过程
do {
var updateLane = update.lane; //lane优先级
//...more...
if (update.hasEagerState) {
// If this update is a state update (not a reducer) and was processed eagerly,
// we can use the eagerly computed state
newState = update.eagerState;
} else {
//核心代码就在这,
var action = update.action; //取得当前的update的action,可能是函数也可能是具体的值
newState = reducer(newState, action);
}
}
update = update.next; //更新update指针指向下一个更新
} while (update !== null && update !== first); //条件就是把整条环转链表更新完
//把最终得倒的状态更新到 hook上
hook.memoizedState = newState;
hook.baseState = newBaseState;
hook.baseQueue = newBaseQueueLast;
queue.lastRenderedState = newState;
}
var dispatch = queue.dispatch;
//返回state和dispatch
return [hook.memoizedState, dispatch];
}
从新梳理一下
-
通过updateWorkInProgressHook 方法更新 当前执行的hook
-
把baseQueue和pendingQueue合并,(baseQueue,为之前因为某些原因导致更新中断从而剩下的update链表,pendingQueue则是本次产生的update链表,将他们首位合并形成新的链表,注意在 do. while 循环之前的第一个firstUpdate 变量赋值的是baseQueue.next,说明了整个合并的链表是先执行上一次更新剩下的在执行新的,这样我们的调用拿到的才是正确的数据,举个例子, 这个例子中每次调用setName,我都能拿到上一次的oldState
-
通过循环遍历每次产生的newState,作为下一次的参数,直到遍历完整个链表
-
最后更新hook上的参数,返回state和dispatch
const [name,setName] = useState('yangxiang')
setName((oldState)=>{ return oldState + 'hhaha ' //此时oldState == 'yangxiang' })
setName((oldState)=>{ return oldState + '奥德曼' //此时oldState == 'yangxianghhaha '})
上述代码中的reducer参数之前说过是basicStateReducer()方法,具体看看他都做了啥
function basicStateReducer(state, action) {
return typeof action === 'function' ? action(state) : action;
}
完整代码就这..., 好吧其实就是判断了一下action是不是函数如果是,则执行函数并把state作为参数,如果不是则直接返回action。
useState简单实现
let isMount = true; //模拟hook mount时期let workInProgressHook = null; //const fiber = { memoizedState: null, stateNode: App};
//模拟调度 在执行dispatch 函数后重新执行 函数function schedule() { isMount = false
workInProgressHook = fiber.memoizedState; fiber.stateNode();}function useState(initailState) { let hook; if (isMount) { //mount时期 hook = { queue: { pending: null }, memoizedState: typeof initailState == "function" ? initailState() : initailState, next: null }; if (!fiber.memoizedState) { fiber.memoizedState = hook; } else { workInProgressHook.next = hook; } workInProgressHook = hook; } else { // update时期 hook = workInProgressHook; workInProgressHook = workInProgressHook.next; } let baseState = hook.memoizedState; // console.log(hook.queue.pending) if (hook.queue.pending) { //遍历执行所有的update let currentUpdate = hook.queue.pending.next; do { let action = currentUpdate.action; baseState = action(baseState); // console.log('baseState',baseState,typeof baseState) currentUpdate = currentUpdate.next; } while (currentUpdate != null && currentUpdate != hook.queue.pending.next); hook.queue.pending = null; } hook.memoizedState = baseState; return [baseState, dispatchSetState.bind(null, hook.queue)];}function dispatchSetState(queue, action) { // console.log('action-->',action) let update = { action: action, next: null }; let pending = queue.pending; if (pending == null) { update.next = update; } else { update.next = pending.next; pending.next = update; } queue.pending = update; schedule();}function App() { const [name, setName] = useState(1); console.log("name", name); appendDom(`currentValue----> ${name}`); return { onclick: () => { setName((oldVale) => { console.log("oldValue", oldVale); appendDom(`oldvalue ---> ` + oldVale); return oldVale + 1; }); } };}let obj = App();obj.onclick();obj.onclick();obj.onclick();obj.onclick();
demo 地址
useReducer
useState的替代方案。它接收一个形如(state, action) => newState的 reducer,并返回当前的 state 以及与其配套的dispatch方法。(如果你熟悉 Redux 的话,就已经知道它如何工作了。)
在某些场景下,useReducer会比useState更适用,例如 state 逻辑较复杂且包含多个子值,或者下一个 state 依赖于之前的 state 等。并且,使用useReducer还能给那些会触发深更新的组件做性能优化,因为你可以向子组件传递dispatch而不是回调函数 。
const initialState = {count: 0};
function init(initialCount) { return {count: initialCount};}
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
const [state, dispatch] = useReducer(reducer, initialState,init);//惰性初始化
return (
<>
Count: {state.count}
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
</>
);
}
这是一个基本使用
mountReducer
function mountReducer(reducer, initialArg, init) {
var hook = mountWorkInProgressHook(); //初始化hook,和之前 mouState一样的
var initialState;
//初始化initialState 赋值
//按照上方的例子 initialArg = initialState = {count: 0}
if (init !== undefined) {
initialState = init(initialArg); //init 是做惰性初始化的具体可看官网
} else {
initialState = initialArg;
}
hook.memoizedState = hook.baseState = initialState;
var queue = {
pending: null,
interleaved: null,
lanes: NoLanes,
dispatch: null,
lastRenderedReducer: reducer,
lastRenderedState: initialState
};
hook.queue = queue;
var dispatch = queue.dispatch = dispatchReducerAction.bind(null, currentlyRenderingFiber$1, queue);
return [hook.memoizedState, dispatch];
}
-
和mountState 一样 初始化一个hook,其实其他的hook方法在mount时第一步也是如此。
-
判断是否进行惰性加载,然后初始化initialState
-
hook.memoizedState = hook.baseState = initialState 同时生成一个queue数据结构
-
调用dispatchReducerAction,返回dispatch
-
最后return return [hook.memoizedState, dispatch];
有上述可知其实和mountState差不多,需要关注的是dispatchReducerAction这个方法。
function dispatchReducerAction(fiber, queue, action) {
{
if (typeof arguments[3] === 'function') {
error("State updates from the useState() and useReducer() Hooks don't support the " + 'second callback argument. To execute a side effect after ' + 'rendering, declare it in the component body with useEffect().');
}
}
var lane = requestUpdateLane(fiber);
var update = {//生成一个update
lane: lane,
action: action,
hasEagerState: false,
eagerState: null,
next: null
};
if (isRenderPhaseUpdate(fiber)) {
enqueueRenderPhaseUpdate(queue, update);
} else {
enqueueUpdate$1(fiber, queue, update);
var eventTime = requestEventTime();
var root = scheduleUpdateOnFiber(fiber, lane, eventTime);
if (root !== null) {
entangleTransitionUpdate(root, queue, lane);
}
}
markUpdateInDevTools(fiber, lane);
}
其实和之前的在mountState 中调用的 dispatchSetState 方法逻辑是差不多的,做的事情也是差不多的,这里就不再重复的讲了
useEffect
之前在讲解useState阶段曾经说过有个update 数据结构,而在这里 也有一个 特殊的数据结构
effect,他和hook,fiber,的关系 和useState的update差不多
var effect = {
tag: tag, //副作用标记
create: create, //传入的方法
destroy: destroy, //return的销毁方法
deps: deps, // 依赖项
next: null //下一个effect
};
mountEffect
话不多说直接看代码
function mountEffect(create, deps) {
if ( (currentlyRenderingFiber$1.mode & StrictEffectsMode) !== NoMode) {
return mountEffectImpl(MountPassiveDev | Passive | PassiveStatic, Passive$1, create, deps);
} else {
return mountEffectImpl(Passive | PassiveStatic, Passive$1, create, deps);
}
}
这就是mouthEffect 全部代码,create 参数就是我们传给useEffect(fn,[])的函数,deps参数就是依赖项。
-
首先对当前fiber.mode 与 StrictEffectsMode 严格模式进行与运算,其中的区别就是副作用的差别一个多了一个
MountPassiveDev,另一个没有(目前不太理解这一步的具体原因,有知道的大佬麻烦留言告诉我一下) -
之后就是调用mountEffectImpl 这个函数实现
function mountEffectImpl(fiberFlags, hookFlags, create, deps) { var hook = mountWorkInProgressHook(); var nextDeps = deps === undefined ? null : deps; currentlyRenderingFiber$1.flags |= fiberFlags; hook.memoizedState = pushEffect(HasEffect | hookFlags, create, undefined, nextDeps); }
由这个源代码可知
- 首先生成调用mountWorkInProgressHook() 生成一个hook
- 判断依赖项是否为空
- 进行副作用合并 ,并赋值给fiber
- 调用pushEffect(),将useEffect生成的effect 链表挂载到hook.memoizedState上
接下来看pushEffect
function pushEffect(tag, create, destroy, deps) {
var effect = {
tag: tag,
create: create,
destroy: destroy,
deps: deps, // Circular
next: null
};
var componentUpdateQueue = currentlyRenderingFiber$1.updateQueue;
if (componentUpdateQueue === null) {
componentUpdateQueue = createFunctionComponentUpdateQueue();
currentlyRenderingFiber$1.updateQueue = componentUpdateQueue;
//生成环转链表,然后将componentUpdateQueue.lastEffect指针指向effect环状链表的最后一个
//这里和之前 useState生成 update 换装链表的过程一样
componentUpdateQueue.lastEffect = effect.next = effect;
} else {
var lastEffect = componentUpdateQueue.lastEffect;
if (lastEffect === null) {
//这里同样是生成effect 环状链表的过程
componentUpdateQueue.lastEffect = effect.next = effect;
} else {
//对生成的effect 插入链表尾部的操作
var firstEffect = lastEffect.next;
lastEffect.next = effect;
effect.next = firstEffect;
componentUpdateQueue.lastEffect = effect;
}
}
return effect;
}
- 生成一个effect 的数据结构
- 判断currentlyRenderingFiber$1.updateQueue 是否为空,至于updateQueue上具体是啥,后面你就知道了
- 其实就是根据不同条件执行 生成effect 环状链表 ,还是把生成的 effect 插入链表尾部的过程,从上面源码可知对于functionComponent ,fiber.updateQueue.lastEffect指针就是指向当前functionComponent的effect链表的最后一个 effect
createFunctionComponentUpdateQueue()方法,从名字就能看出就是创建一个updateQueue,接下来看看具体怎么实现的
function createFunctionComponentUpdateQueue() {
return {
lastEffect: null,
stores: null
};
}
好了非常nice,就这。。。
updateEffect
function updateEffect(create, deps) {
return updateEffectImpl(Passive, Passive$1, create, deps);
}
function updateEffectImpl(fiberFlags, hookFlags, create, deps) {
var hook = updateWorkInProgressHook();
var nextDeps = deps === undefined ? null : deps;
var destroy = undefined;
if (currentHook !== null) {
//在之前介绍mountEffectImpl时生成的 effect 是挂载到 hook.memoizedState 属性上的
//这里拿到之前的 effect ,同时如果有destory方法,也就是我们return的那个方法也拿出来
var prevEffect = currentHook.memoizedState;
destroy = prevEffect.destroy;
if (nextDeps !== null) {
var prevDeps = prevEffect.deps;
if (areHookInputsEqual(nextDeps, prevDeps)) {//通过areHookInputsEqual方法比较依赖项是否改变
hook.memoizedState = pushEffect(hookFlags, create, destroy, nextDeps);
return;
}
}
}
currentlyRenderingFiber$1.flags |= fiberFlags;
hook.memoizedState = pushEffect(HasEffect | hookFlags, create, destroy, nextDeps);
}
在updateEffectImpl中
- 调用updateWorkInProgressHook(),之前讲过,更新当前正在执行的hook,也就是workInprogressHook
- 处理依赖项
- 命中if判断后进入其中主要做了,在之前介绍mountEffectImpl时生成的 effect 是挂载到 hook.memoizedState 属性上的 ,这里拿到之前的 effect ,同时如果有destory方法,也就是我们return的那个方法也拿出来,然后通过 areHookInputsEqual(nextDeps,prevDeps)比较前后依赖项有没有改变
- 同样是调用pushEffect 方法,和之前讲的一样
useRef
function mountRef(initialValue) {
var hook = mountWorkInProgressHook();
{
var _ref2 = {
current: initialValue
};
hook.memoizedState = _ref2;
return _ref2;
}
}
function updateRef(initialValue) {
var hook = updateWorkInProgressHook();
return hook.memoizedState;
}
从上面的代码可以知道,通过useRef 创建的 ref ,仅仅就是一个包含 一个 current 属性的 对象,同时挂在到hook 的memoizedState 属性上,update时直接返回 挂载在 hook.memoizedState上的 对象,这也就解释了为什么ref ,在整个组件的生命周期内不会发生改变,除非我们手动的通过 ref.current = 'xxx' 去修改。
useCallback/useMemo
mountCallback/mountMemo
function mountCallback(callback, deps) { var hook = mountWorkInProgressHook(); var nextDeps = deps === undefined ? null : deps; hook.memoizedState = [callback, nextDeps]; return callback;}
function mountMemo(nextCreate, deps) { var hook = mountWorkInProgressHook(); var nextDeps = deps === undefined ? null : deps; var nextValue = nextCreate(); hook.memoizedState = [nextValue, nextDeps]; return nextValue;}
代码一看就懂,区别就是一个执行了函数返回值,一个直接返回函数
updateCallback/updateMemo
function updateCallback(callback, deps) { var hook = updateWorkInProgressHook(); var nextDeps = deps === undefined ? null : deps; var prevState = hook.memoizedState; if (prevState !== null) { if (nextDeps !== null) { var prevDeps = prevState[1]; if (areHookInputsEqual(nextDeps, prevDeps)) { //比较前后依赖 如果相同就返回之前保存的 return prevState[0]; } } } //否则就返回 新的 callback hook.memoizedState = [callback, nextDeps]; return callback;}
function updateMemo(nextCreate, deps) { var hook = updateWorkInProgressHook(); var nextDeps = deps === undefined ? null : deps; var prevState = hook.memoizedState; if (prevState !== null) { // Assume these are defined. If they're not, areHookInputsEqual will warn. if (nextDeps !== null) { var prevDeps = prevState[1]; if (areHookInputsEqual(nextDeps, prevDeps)) { //比较 前后依赖项 ,相同返回之前的值 return prevState[0]; } } } //否则 返回 新的值,并把新的值和依赖项保存在hook.memoizedState上
var nextValue = nextCreate(); hook.memoizedState = [nextValue, nextDeps]; return nextValue;}