下面分析16.14.2版本react中useState的源码实现
介绍
数据结构
当前fiber节点的数据结构是:
1 | const fiberNode = { |
React 的 Hooks 是一个单向链表,Hook.next 指向下一个 Hook。
1 | Fiber.memoizedState => hook1 state1 === hook1.memoizedState |
入口文件
react/cjs/react.development.js
1 | function useState(initialState) { |
resolveDispatcher返回的是ReactCurrentDispatcher.current,所以useState = ReactCurrentDispatcher.current.useState
首次渲染
ReactDOM.render(<App/>, rootElement)
,react从render开始执行
1 | render => ... => beginWork => updateFunctionComponent => renderWithHooks |
hooks的核心渲染逻辑入口是renderWithHooks。
renderWithHooks
1 | // 14762 |
从上述代码可知,首次挂载时,useState = HooksDispatcherOnMountInDEV.useState = mountState
;state更新时,useState = HooksDispatcherOnUpdateInDEV.useState = mountState
mountState
1 | // 15213 第一次调用组件的 useState 时实际调用的方法 |
(1)执行mountWorkInProgressHook(),更新hook链表,并返回当前 workInProgressHook
1 | // 14921 创建一个新的 hook,并返回当前 workInProgressHook |
(2)获取当前的workInProgressHook后,初始化了hook.memoizedState、hook.baseState、hook.queue和queue.dispatch
(3)basicStateReducer的源码如下:
1 | // 15007 |
(4)最终返回一个数组 [hook.memoizedState, dispatch]
执行setName('queen')
,其实就是执行dispatch('queen')
第一次const [name, setName] = useState('king')
,mountWorkInProgressHook()后,hook的值为
1 | hook = { |
第二次const [count, setCount] = useState(0)
,mountWorkInProgressHook()后,hook的值为
1 | hook = { |
dispatchAction
以const [name, setName] = useState('king') setName('queen')
为例:
执行setName('queen')
,其实就是执行dispatchAction(fiber, queue, 'queen')
- dispatchAction() 会创建 update 对象({action:’queen’})
- 将 update 加至 hook.queue 的末尾:hook.queue.pending = update
- 执行 scheduleWork(),走 updateFunctionComponent() 流程
核心代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32// 15564
function dispatchAction(fiber, queue, action) {
// action 就是传进来要更新的 state->'queen'
var update = {
expirationTime: expirationTime,
suspenseConfig: suspenseConfig,
action: action,
eagerReducer: null,
eagerState: null,
next: null
};
// 这里的queue,是之前传入的hook对象中的queue,这里保留了一个引用!!(即queue发生变化,当前fiber节点的hook数据也是同步变更的)
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;
}
// 将update对象加至hook.queue的末尾pending中
queue.pending = update;
var currentState = queue.lastRenderedState;
// currentState: king,action: queen
// 将新的state queen 赋值到 update.eagerState
var eagerState = lastRenderedReducer(currentState, action);
update.eagerReducer = lastRenderedReducer;
update.eagerState = eagerState;
// ...
scheduleWork(fiber, expirationTime)
}setName('queen')
,即执行dispatchAction()后,hook变为最后会执行scheduleWork(fiber, expirationTime),经过React的调度,会带上action(setName的传参),再次进入hook组件核心渲染逻辑:renderWithHooks。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24hook = {
baseQueue: null,
baseState: 'king',
memoizedState: 'king',
queue: {
lastRenderedReducer: basicStateReducer(state, action),
lastRenderedState: 'king',
pending: {
action: 'queen',
eagerState: 'queen',
next: pending // pedding对象,单链表
}
},
next: {
baseQueue: null,
baseState: 0,
memoizedState: 0,
queue: {
lastRenderedReducer: basicStateReducer(state, action),
lastRenderedState: 0,
pending: null
},
},
}此时,由于并非首次渲染组件,React会使用HooksDispatcherOnUpdateInDEV对象上的useState,1
2dispatchAction => scheduleUpdateOnFiber => ensureRootIsScheduled => performConcurrentWorkOnRoot => performUnitOfWork => beginWork => updateFunctionComponent
=> renderWithHooksuseState = HooksDispatcherOnUpdateInDEV.useState = mountState
。在这个useState中,会使用一个叫做updateState的函数来更新最新的state值。renderWithHooks()中有一个Component()方法,用来执行App(),此时又会执行1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18// 14762
renderWithHooks()
// 14787 核心逻辑
if (current !== null && current.memoizedState !== null) {
ReactCurrentDispatcher.current = HooksDispatcherOnUpdateInDEV;
} else { // 组件首次挂载时
ReactCurrentDispatcher.current = HooksDispatcherOnMountInDEV;
}
=>
15694 HooksDispatcherOnMountInDEV.useState = {
return mountState(initialState) // 15766
}
15881 HooksDispatcherOnUpdateInDEV.useState = {
return updateState(initialState) // 15939
}
// ...
var children = Component(props, secondArg)
}const [name, setName] = useState('king')
,最终调用的是HooksDispatcherOnUpdateInDEV.useState(king') => updateState('king')
。更新
updateState()
由updateState可以看出,实际调用的是 updateReducer1
2
3
4
5
6
7// 15233
function updateState(initialState) {
return updateReducer(basicStateReducer);
}
function basicStateReducer(state, action) {
return typeof action === 'function' ? action(state) : action;
}updateReducer()
updateReducer的核心代码:(1)第一步执行updateWorkInProgressHook(),获取当前正在工作中的 hook1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18// 15033
funtion updateReducer() {
var hook = updateWorkInProgressHook();
var queue = hook.queue;
var pendingQueue = queue.pending
current.baseQueue = baseQueue = pendingQueue;
queue.pending = null;
var first = baseQueue.next;
var newState = current.baseState;
var update = first;
if (update.eagerReducer === reducer) {
newState = update.eagerState;
}
hook.memoizedState = newState;
hook.baseState = newBaseState;
var dispatch = queue.dispatch;
return [hook.memoizedState, dispatch]; // 返回['queen', dispatch]
}第一次1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36// 14941
function updateWorkInProgressHook() {
// 此时currentlyRenderingFiber$1.memoizedState,但是为fiber的副本,保留着fiber.memoizedState的内容
// 核心代码
var nextCurrentHook;
if (currentHook === null) { // 第一次useState
var current = currentlyRenderingFiber$1.alternate;
if (current !== null) {
nextCurrentHook = current.memoizedState;
} else {
nextCurrentHook = null;
}
} else {
nextCurrentHook = currentHook.next;
}
var nextWorkInProgressHook;
if (workInProgressHook === null) { // 第一次useState
nextWorkInProgressHook = currentlyRenderingFiber$1.memoizedState;
} else {
nextWorkInProgressHook = workInProgressHook.next;
}
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
}const [name, setName] = useState('king')
,workInProgressHook的值为第二次1
2
3
4
5
6
7
8
9
10
11workInProgressHook={
memoizedState: "king",
baseState: "king",
queue:{
pending:{
action: "queen",
eagerState: "queen",
}
},
next: null
}const [count, setCount] = useState(0)
,workInProgressHook的值为(2)从workInProgressHook的数据结构可以看出,我们需要更新的值就在queue.pending.eagerState/action中1
2
3
4
5
6
7
8
9
10
11workInProgressHook={
memoizedState: 0,
baseState: 0,
queue:{
pending:{
action: 0,
eagerState: 0,
}
},
next: null
}(3)更新state1
2
3if (update.eagerReducer === reducer) {
newState = update.eagerState;
}将1
2
3
4hook.memoizedState = newState;
hook.baseState = newBaseState;
var dispatch = queue.dispatch;
return [hook.memoizedState, dispatch]; // 返回['queen', dispatch]'queen'
赋值给hook.memoizedState
,返回['queen', diapatch]
,此时的name
已经更新为'queen'
由上述源码可知,useState按顺序执行的原因是,useState是通过next来查找下一个hooks