React hooks简单源码实现
虚拟DOM上保存了自身组件和hooks,如果有多个hooks则以链表的形式保存。当使用updateNum()等更新函数的时候,调用dispatchAction函数,在hooks.queue.pending里面储存环状链表来表示需要更新的值,然后从新调用schedule函数,再次执行组件的更新,改变hooks的状态值。
let isNount = true;
let workInProgressHook = null;//储存所有的hooks,以链表的形式
const fiber = {//表示虚拟DOM
stateNode: App,//自身
memoizedState: null,
}
//useState hooks
function useState(initialState) {//initialState是useState初始化值
let hooks;
if (isNount) {//首次渲染
hooks = {
memoizedState: initialState,//保存初始化值
next: null,//保存下一个节点
queue: {
pending: null//保存状态的改变,为环状链表
}
}
if (!fiber.memoizedState) {//如果是组件中的第一个hooks
fiber.memoizedState = hooks;//将hooks赋值给fiber.memoizedState
workInProgressHook = hooks;//将hooks赋值给workInProgressHook
} else {//如果不是组件中的第一个hooks
workInProgressHook.next = hooks;//则将workInProgressHook连接下一个链表节点
}
workInProgressHook = hooks;//将workInProgressHook重新赋值为尾节点
} else {//不是首次渲染
hooks = workInProgressHook;//将hooks赋值上第一次创建的useState
workInProgressHook = workInProgressHook.next;//workInProgressHook指向下一个节点
}
let baseState = hooks.memoizedState;//获取hooks上一次的状态
if (hooks.queue.pending) {//如果更新,表示有新的update
let firstUpdate = hooks.queue.pending.next;//找到环状链表的第一个update
do {
const action = firstUpdate.action;
baseState = action(baseState);//获取更新函数里面的值,updateNum(num => num + 1);里面的值就是num => num + 1
firstUpdate = firstUpdate.next;//让链表指向下一个节点
} while (firstUpdate !== hooks.queue.pending.next);//结束环状链表遍历
hooks.queue.pending = null;//更新完毕,赋值为null
}
hooks.memoizedState = baseState;//将hooks.memoizedState赋值为新的值
return [baseState, dispatchAction.bind(null, hooks.queue)];//返回值和更新函数
}
function dispatchAction(queue, action) {
const update = {//环状链表
action,//调用更新函数里面的传参
next: null
}
if (queue.pending === null) {//没有触发更新,触发第一个更新
//u0 -> u0 -> u0
update.next = update;//让自身的下一个节点指向自身,形成环状链表
} else {
//如果有多个更新,update保存的是最后一个更新函数,则update.next保存第一个更新函数才能形成环状链表
update.next = queue.pending.next;//u0 -> u1
queue.pending.next = update;//u1 -> u0
//形成环状链表
}
queue.pending = update;//将queue.pending赋值为环状链表的最后一个节点
schedule();//调用函数重新加载数据
}
//当首次渲染和更新的时候调用schedule
function schedule() {
workInProgressHook = fiber.memoizedState;//workInProgressHook储存所有的hooks,以链表的形式。
const app = fiber.stateNode();//执行App()。
isNount = false;//将isNount设置成false,表示以后不是第一层渲染。
return app;//返回APP()执行后的结果,我们模拟APP函数只有onClick和onFocus函数。
}
function App() {
const [num, updateNum] = useState(0);
const [num1, updateNum1] = useState(10);
console.log('num:', num);
console.log('num1:', num1);
return {
onClick() {
updateNum(num => num + 1);
},
onFocus() {
updateNum1(num1 => num1 + 10);
}
}
}
window.app = schedule();
app.onClick();
app.onFocus();