最近在看 React Hooks 源码,并参照大神卡颂的B站视频,依照源码实现了简单的 useState 的实现,直接上代码。
let isMount = true
let workInProgressHook = null
const fiber = {
stateNode: App,
memoizedState: null // 单向链表存储每一个 useXXX 的 hook
}
function useState (initialState) {
let hook
if (isMount) {
hook = {
memoizedState: initialState,
next: null,
// 存储 setXXX 时的队列,pending是待执行的循环链表
queue: {
pending: null
}
}
if (!fiber.memoizedState) {
fiber.memoizedState = hook
} else {
workInProgressHook.next = hook
}
// 上一行代码,workInProgressHook 可以视为上一次的 hook
// 将上一次的 hook 的 next 指向当前的 hook
// 并将当前的 hook 的引用赋给 workInProgressHook
workInProgressHook = hook
} else {
hook = workInProgressHook
workInProgressHook = workInProgressHook.next
}
let baseState = hook.memoizedState
if (hook.queue.pending) {
let firstUpdate = hook.queue.pending.next
do {
const action = firstUpdate.action
baseState = action(baseState)
firstUpdate = firstUpdate.next
} while (firstUpdate !== hook.queue.pending)
hook.queue.pending = null
}
hook.memoizedState = baseState
return [baseState, dispatchAction.bind(null, hook.queue)]
}
function dispatchAction (queue, action) {
// 环状链表,链表中的每一个 action 可能执行的优先级不同
// 因为优先级不同,可能会跳过一些节点执行优先级高的节点
// 环状链表可以完成被跳过节点的执行
// 而单向链表需要找到第一个节点再继续执行
const update = {
action,
next: null
}
// 生成环状链表
if (queue.pending === null) {
// u0 -> u0
update.next = update
} else {
// u1 -> u0 -> u1
// queue.pending 为环状链表的最后一个节点
// queue.pending.next 即为环状链表的第一个节点
// u0 -> u1 -> u2 -> u0, 添加 u3 后,环状为 u0 -> u1 -> u2 -> u3 -> u0
// 其中 u3: update, u2: queue.pending, u0: queue.pending.next
// u3.next = u0, u2.next = u3
update.next = queue.pending.next
queue.pending.next = update
}
queue.pending = update
schedule()
}
function schedule () {
// 渲染完成后将 workInProgressHook 设置回链表的第一个位置
workInProgressHook = fiber.memoizedState
const app = fiber.stateNode()
isMount = false
return app
}
function App () {
const [num, updateNum] = useState(1)
const [num1, updateNum1] = useState(10)
console.log('isMount?', isMount)
console.log('num:', num)
console.log('num1:', num1)
return {
onClick () {
updateNum(num => num + 1)
updateNum1(num => num + 10)
}
}
}
window.app = schedule()
代码中的一些注释是根据自己的理解写进去的,不一定完全正确,欢迎路过的大佬指正。
附上 卡颂老师的B站主页
卡颂老师视频分享里面会更加详细的介绍迷你版 useState 的实现过程,并有源码解读。感谢卡颂老师的分享。