以 useState 为例的 hooks 源码

简单的 useState 源码

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
// 区分是 update 还是 mount
let isMount = true

// 指针,指向 memorizedState 中正在工作的 hook
let workInProgressHook = null

// 每个组件都有一个 fiber 对象,这里是 App 的 Fiber对象
const fiber = {
stateNode: App,
// 用链表的结构 保存所有的 State(函数式组件就是 useState 的数据,类组件就是 this.state 中的数据)
memorizedState: null
}

function useState(initialState) {
let hook

/* 1.取老 state */
// 如果是 Mount,需要构建链表
if (isMount) {
// hook 为链表的一个节点
hook = {
// 当前状态
memorizedState: initialState,
next: null,
// 接下来会产生的状态变更。之所以是队列,因为可能连续触发多次状态的更新
queue: {
pending: null
}
}
// 构建链表
if (!fiber.memorizedState) {
fiber.memorizedState = hook
workInProgressHook = hook
} else {
workInProgressHook.next = hook
}
workInProgressHook = hook
} else {
// 如果是 Update,确定当前 useState 的 hook 节点
hook = workInProgressHook
workInProgressHook = workInProgressHook.next
}
// 取当前 hook 的状态
let baseState = hook.memorizedState

/* 2.计算新的 state */
if (hook.queue.pending) {
let firstUpdate = hook.queue.pending.next

do {
const action = firstUpdate.action
// 执行 action,更新 state
baseState = action(baseState)
firstUpdate = firstUpdate.next
} while (firstUpdate !== hook.queue.pending.next)

hook.queue.pending = null
}

hook.memorizedState = baseState
return [baseState, dispatchAction.bind(null, hook.queue)]
}


function dispatchAction(queue, action) {
// queue 表示原先的待更新队列,action 为这次传进来的更新
// update 是一个环状链表。为什么?因为更新有优先级
const update = {
action,
next: null
}

// 构建与更新环状链表
if (queue.pending === null) {
update.next = update
} else {
update.next = queue.pending.next
queue.pending.next = update
}
queue.pending = update

// 调用调度器
schedule()
}

// 调度器
function schedule() {
// workInProgressHook 指针复位,重新指向第一个
workInProgressHook = fiber.memorizedState
// 触发组件的 render
fiber.stateNode()
isMount = false
}

function App() {
const [num1, updateNum1] = useState(0)
const [num2, updateNum2] = useState(10)

return {
onClick() {
// updateNum(num + 1)
updateNum1(num => num + 1)
updateNum1(num => num + 1)
updateNum1(num => num + 1)
}
}

// return <p onClick={() => updateNum(num + 1)}></p>
}

window.app = schedule()

为什么组件 render 时,useState 的状态不会被重置?

因为状态是被维护在组件外部的 Fiber 对象中的 memorizedState 这个属性中的,我们在组件内使用的只是对外层对象中属性的引用(闭包)。

参考

https://www.bilibili.com/video/BV1iV411b7L1/?spm_id_from=333.337.search-card.all.click&vd_source=1616b746cefe2e7615c229563ba38eb4


以 useState 为例的 hooks 源码
http://example.com/2024/02/22/以-useState-为例的-hooks-源码/
Author
John Doe
Posted on
February 22, 2024
Licensed under