hooks-useReducer源码
现目前react端都流行hooks写法,但不能只会用而不清楚它是怎么实现的,所以今天我就来研究hooks到底是怎么实现的。源码版本:17.0.2
demo:
function FunctionComponent(props) {
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;
currentlyRenderingFiber$1 = workInProgress;
workInProgress.memoizedState = null;
workInProgress.updateQueue = null;
workInProgress.lanes = NoLanes;
ReactCurrentDispatcher$1.current = HooksDispatcherOnMountInDEV;
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 {
ReactCurrentDispatcher$1.current = HooksDispatcherOnMountInDEV;
}
children = Component(props, secondArg);
} while (didScheduleRenderPhaseUpdateDuringThisPass);
}
return children;
}
hooks-useReducer
const [state, dispatch] = useReducer((x)=>x+1, 1);
和hooks相关的全局变量。
currentlyRenderingFiber$1
currentHook;
workInProgressHook;
ReactCurrentDispatcher$1
- useReducer,不管是mount还是update都是这样的结构,只不过mount是mountReducer,update是updateReducer,
- mount会将我们的hooks一个一个的以链表的形式绑定到fiber的memoizedState
useReducer: function (reducer, initialArg, init) {
currentHookNameInDev = 'useReducer';
mountHookTypesDev();
var prevDispatcher = ReactCurrentDispatcher$1.current;
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV;
try {
return mountReducer(reducer, initialArg, init);
} finally {
ReactCurrentDispatcher$1.current = prevDispatcher;
}
},
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
function mountWorkInProgressHook() {
var hook = {
memoizedState: null,
baseState: null,
baseQueue: null,
queue: null,
next: null
};
if (workInProgressHook === null) {
currentlyRenderingFiber$1.memoizedState = workInProgressHook = hook;
} else {
workInProgressHook = workInProgressHook.next = hook;
}
return workInProgressHook;
}
function mountReducer(reducer, initialArg, init) {
var hook = mountWorkInProgressHook();
var initialState;
if (init !== undefined) {
initialState = init(initialArg);
} else {
initialState = initialArg;
}
hook.memoizedState = hook.baseState = initialState;
var queue = hook.queue = {
pending: null,
dispatch: null,
lastRenderedReducer: reducer,
lastRenderedState: initialState
};
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
};
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)) {
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);
update.eagerReducer = lastRenderedReducer;
update.eagerState = eagerState;
} catch (error) {
} finally {
{
ReactCurrentDispatcher$1.current = prevDispatcher;
}
}
}
}
scheduleUpdateOnFiber(fiber, lane, eventTime);
}
}
updateReducer
依靠的是我们在初始化时绑定的currentlyRenderingFiber$1上面的memoizedState,来更改当前hooks的,workInProgressHook和currentHook
function updateWorkInProgressHook() {
var nextCurrentHook;
if (currentHook === null) {
var current = currentlyRenderingFiber$1.alternate;
if (current !== null) {
nextCurrentHook = current.memoizedState;
} else {
nextCurrentHook = null;
}
} else {
nextCurrentHook = currentHook.next;
}
var nextWorkInProgressHook;
if (workInProgressHook === null) {
nextWorkInProgressHook = currentlyRenderingFiber$1.memoizedState;
} else {
nextWorkInProgressHook = workInProgressHook.next;
}
if (nextWorkInProgressHook !== null) {
workInProgressHook = nextWorkInProgressHook;
nextWorkInProgressHook = workInProgressHook.next;
currentHook = nextCurrentHook;
} else {
currentHook = nextCurrentHook;
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) {
var hook = updateWorkInProgressHook();
var queue = hook.queue;
queue.lastRenderedReducer = reducer;
var current = currentHook;
var baseQueue = current.baseQueue;
var pendingQueue = queue.pending;
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;
newState = reducer(newState, action);
}
}
update = update.next;
} while (update !== null && update !== first);
if (newBaseQueueLast === null) {
newBaseState = newState;
} else {
newBaseQueueLast.next = newBaseQueueFirst;
}
if (!objectIs(newState, hook.memoizedState)) {
markWorkInProgressReceivedUpdate();
}
hook.memoizedState = newState;
hook.baseState = newBaseState;
hook.baseQueue = newBaseQueueLast;
queue.lastRenderedState = newState;
}
var dispatch = queue.dispatch;
return [hook.memoizedState, dispatch];
}
几句话说明白useReducer
-
区分为初始化和更新。初始化,在我们第一次进行调度时,检测到是函数组件,调用renderWithHooks,执行useReducer,调用mountReducer操作,将传入的初始值挂载到fiber的memoizedState, -
更新的时候,dispatch会去触发scheduleUpdateOnFiber,进入调度,再次进到renderWithHooks,执行updateReducer,得到新的state值返回,并重新计算渲染。
|