如果当前FunctionComponent
正在render
时,我们触发了dispatch
。则会在当前render
结束后在重新执行一次render
,完全没必要再重新走一次完整的dispatch
链路,这个过程我们称为re-render
那么这一切是如何实现的呢,首先,我们生成一个map
用来存储这些需要re-render
时需要的update
,映射queue
与任务队列。在dispatch
中的逻辑如下
function dispatchAction<S, A>(fiber: Fiber, queue: UpdateQueue<S, A>, action: A) {
const { alternate } = fiber
// 当前处于render状态
if (fiber === currentlyRenderingFiber || (alternate !== null && alternate === currentlyRenderingFiber)) {
didScheduleRenderPhaseUpdate = true // 触发re-render
const update: Update<S, A> = {
expirationTime: renderExpirationTime,
action,
eagerReducer: null,
eagerState: null,
next: null,
}
// 生成 queue 的映射队列
if (renderPhaseUpdates === null) {
renderPhaseUpdates = new Map()
}
// 放入 map
const firstRenderPhaseUpdate = renderPhaseUpdates.get(queue)
if (firstRenderPhaseUpdate === undefined) {
renderPhaseUpdates.set(queue, update)
} else {
let lastRenderPhaseUpdate = firstRenderPhaseUpdate
while (lastRenderPhaseUpdate.next !== null) {
lastRenderPhaseUpdate = lastRenderPhaseUpdate.next
}
lastRenderPhaseUpdate.next = update
}
} else {
...
}
}
在renderWithHooks
中,判断didScheduleRenderPhaseUpdate
是否需要触发re-render
,如果需要re-render
的话会把相应的全局变量重置,并再执行一次component()
function renderWithHooks(current: Fiber, workInProgress: Fiber, Component: Function, props: any, refOrContext: any, nextRenderExpirationTime: ExpirationTime): any {
renderExpirationTime = nextRenderExpirationTime
currentlyRenderingFiber = workInProgress
nextCurrentHook = current !== null ? current.memoizedState : null
ReactCurrentDispatcher.current = nextCurrentHook === null ? HooksDispatcherOnMount : HooksDispatcherOnUpdate
let children: any = Component(props, refOrContext)
if (didScheduleRenderPhaseUpdate) {
do {
didScheduleRenderPhaseUpdate = false
numberOfReRenders += 1
// 从链表头开始
nextCurrentHook = current !== null ? current.memoizedState : null
nextWorkInProgressHook = firstWorkInProgressHook
currentHook = null
workInProgressHook = null
componentUpdateQueue = null
children = Component(props, refOrContext)
} while (didScheduleRenderPhaseUpdate)
renderPhaseUpdates = null
numberOfReRenders = 0
}
...
}
最后,在updateReducer
中,由于普通的update
我们在第一次render
中已全部处理过,所以我们只需要针对map
中的update
进行处理就好
updateReducer<S, A>(reducer: (s: S, a: A) => S): [S, Dispatch<A>] {
const hook = updateWorkInProgressHook()
const { queue } = hook
if (numberOfReRenders > 0) { // 处于re-render状态
const { dispatch: _dispatch } = queue
if (renderPhaseUpdates !== null) {
const firstRenderPhaseUpdate = renderPhaseUpdates.get(queue)
if (firstRenderPhaseUpdate !== undefined) {
renderPhaseUpdates.delete(queue)
let newState = hook.memoizedState
let update = firstRenderPhaseUpdate
do {
const { action } = update
newState = reducer(newState, action)
update = update.next
} while (update !== null)
if (!Object.is(newState, hook.memoizedState)) { // 标记为更新
markWorkInProgressReceivedUpdate()
}
hook.memoizedState = newState
if (hook.baseUpdate === queue.last) {
hook.baseState = newState
}
queue.eagerReducer = reducer
queue.eagerState = newState
return [newState, _dispatch]
}
}
return [hook.memoizedState, _dispatch]
}
...
}