-
-
Save Sylvenas/28d23025ff369ab63c95cd8a40122d4c to your computer and use it in GitHub Desktop.
react hooks 执行过程,包括mount阶段和updater阶段
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Hook数据结构 | |
*/ | |
interface hook { | |
memoizedState: any; // 保存当前 hook 的 state | |
next: hook; // next 指向下一个 hook | |
queue: queue; // 更新队列 | |
} | |
interface queue { | |
dispatch: Function; // 添加到队列的方法 | |
last: updater; // 最后一个updater,环状链表,lastUpdater.next就是firstUpdater,可以 | |
} | |
interface updater { | |
action: Function | any; // 更新函数 | |
next: updater; // 下一个updater | |
} | |
// hook链表的开头 | |
let firstHook: hook = null; | |
// hook 链表的结尾 | |
let lastHook: hook = null; | |
// 首次渲染完成 | |
let mounted: boolean = false; | |
// 当前正在执行计算的 hook | |
let currentUpdateHook: hook = null; | |
function useState(initialState: any): [any, Function] { | |
if (mounted) { | |
return updateState(); | |
} | |
const hook = mountHookLinkedList(); | |
hook.memoizedState = initialState; | |
const queue = (hook.queue = { | |
last: null, | |
dispatch: null, | |
}); | |
const dispatcher = (queue.dispatch = dispatchAction.bind(null, queue)); | |
return [hook.memoizedState, dispatcher]; | |
} | |
function updateState(): [any, Function] { | |
if (currentUpdateHook === null) { | |
currentUpdateHook = firstHook; | |
} | |
const queue = currentUpdateHook.queue; | |
const last = queue.last; | |
let first = last !== null ? last.next : null; | |
let newState = currentUpdateHook.memoizedState; | |
if (first !== null) { | |
let update = first; | |
do { | |
// 执行每一次更新,去更新状态 | |
const action = update.action; | |
// 函数则调用 | |
if (typeof action === 'function') { | |
newState = action(newState); | |
} else { | |
newState = action; | |
} | |
update = update.next; | |
} while (update !== null && update !== first); | |
} | |
currentUpdateHook.memoizedState = newState; | |
// 关键代码,执行一轮调用之后要把更新队列清空,在下一轮的调用中重新添加队列 | |
queue.last = null; | |
currentUpdateHook = currentUpdateHook.next; | |
const dispatch = queue.dispatch; | |
// 返回最新的状态和修改状态的方法 | |
return [newState, dispatch]; | |
} | |
function mountHookLinkedList() { | |
const hook = { | |
memoizedState: null, | |
next: null, | |
queue: null, | |
}; | |
// 第一次生成 hook,赋值 | |
if (lastHook === null) { | |
firstHook = lastHook = hook; | |
} else { | |
// 第二,三,四次生成 hook,依次挂载到前一个hook的next上 | |
lastHook = lastHook.next = hook; | |
} | |
return lastHook; | |
} | |
function dispatchAction(queue, action) { | |
const updater = { action, next: null }; | |
// 将updater对象添加到循环链表中 | |
const last = queue.last; | |
if (last === null) { | |
// 链表为空,将当前更新作为第一个,并保持循环 | |
updater.next = updater; | |
} else { | |
const first = last.next; | |
if (first !== null) { | |
// 在最新的updater对象后面插入新的updater对象 | |
updater.next = first; | |
} | |
last.next = updater; | |
} | |
// 将表头保持在最新的updater对象上 | |
queue.last = updater; | |
} | |
function TeamsInfo() { | |
const [age, setAge] = useState(18); | |
const [name, setName] = useState('income'); | |
console.log(`Age: ${age}; Name: ${name}`); | |
mounted = true; | |
return [setAge, setName]; | |
} | |
const [setAge, setName] = TeamsInfo(); | |
setAge((x) => x + 1); | |
setAge((x) => x + 11); | |
setAge((x) => x * 2); | |
TeamsInfo(); | |
setAge((x) => x * 2); | |
setName((x) => x + '-girls'); | |
TeamsInfo(); | |
console.log(firstHook); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment