<!DOCTYPE html>
<html lang="en">
<head>
<title>React App</title>
</head>
<body>
<div id="root1"></div>
<div id="root2"></div>
<div id="a">
<span id="b">123</span>
</div>
</body>
<script>
let isMount = true; // 模拟状态,看是挂载还是更新
let workInProgressHook = null; // 全局变量,指向当前正在执行的 hooks ,比如 Function Component 中存在多个 hooks 时
let fiber = { // 改 Function Component 对应的 fiber 对象
memoizedState: null, // 保存 hooks
stateNode: App // 对于 Function Component 来说,stateNode 保存了对应的 Function
}
function run() { // 模拟 schelude render commit
workInProgressHook = fiber.memoizedState; // 当开始工作时,将 workInProgressHook 指向第一个 hooks (fiber.memoizedState),因为 fiber.memoizedState 是一个链表结构
const app = fiber.stateNode(); // 模拟 runder 阶段,触发 Function Component 对应函数
isMount = false;
return app;
}
function dispatchAction(queue, action) {
const update = {
action,
next: null // 用于连接其他 update 形成环状链表
}
if (queue.pending === null) { // 判断是否存在环状链表
update.next = update; // queue.pending 不存在时,自己形成一个环状链表
} else {
// 3 -> 0 -> 1 -> 2 -> 3
// 链表中插入新的 update 值为 4
update.next = queue.pending.next; // 4 的 next 指向第一个 update 0
queue.pending.next = update; // queue.pending 指向的是最后一个 update 3,queue.pending.next 则是把最后一个 update 指向新的 update,这里是把 3.next 指向 4 的 update
// 4 -> 0 -> 1 -> 2 -> 3 -> 4
}
queue.pending = update; // queue.pending 指向的是最后一个 update
run(); // 调用 run 触发整个流程
}
function useState(initialState) {
let hook;
if (isMount) {
hook = {
queue: {
pending: null // 用来保存 update
},
memoizedState: initialState, // 保存 hooks 对应的 state
next: null // 指向下一个 hooks ,这样多个 hooks 可以通过链表连接
}
if (!fiber.memoizedState) { // fiber.memoizedState 以链表形式保存多个 hooks
fiber.memoizedState = hook;
} else {
workInProgressHook.next = hook; // workInProgressHook 在 run 的时候已经指向了第一个 hooks,这里将多个 hooks 连接起来
}
workInProgressHook = hook; // 指向最后一个 hooks
} else { // 和 if 保持一致,hook 指向当前 hook 对象,workInProgressHook 指向下一个 hook 对象
hook = workInProgressHook;
workInProgressHook = workInProgressHook.next;
}
// 下面计算 state
let baseState = hook.memoizedState;
if (hook.queue.pending) { // 如果存在代表 hook 上面有需要计算的 update
let firstUpdate = hook.queue.pending.next; // hook.queue.pending 保存的最后一个 update,hook.queue.pending.next 则是第一个 update
do {
const action = firstUpdate.action; // action 在这里值调用 updateNum 传进来的参数,这里我们传的是 function
baseState = action(baseState); // 传入 baseState 进行计算
firstUpdate = firstUpdate.next; // 指向下一个 update
} while (firstUpdate !== hook.queue.pending.next) // firstUpdate 不等于 hook.queue.pending.next(第一个 update) 则继续计算
hook.queue.pending = null; // 遍历完后将 hook.queue.pending 赋值为 null,代表这些 update 已经计算完了
}
hook.memoizedState = baseState; // 计算完后更新 memoizedState,存储计算结果
return [baseState, dispatchAction.bind(null, hook.queue)]; // dispatchAction 的作用是创建 update,并形成环状链表,形成环状链表后我们在调用 useState 的时候才能根据链表进行计算,计算新的 state
}
function App() {
const [num, updateNum] = useState(0);
console.log('isMount : ', isMount);
console.log('num : ', num);
return {
click() {
updateNum(num => num + 1);
},
}
}
window.app = run();
// app.click() // 模拟点击
// 在 React 源码中 hooks 的工作原理就完全是遵循了这个原理
// useEffect useRef useMemo useCallback 他们的区别在于触发的时机不同,对于不同的 hook memoizedState 保存的数据是不一样的,hook 的计算过程是完全一致的
</script>
</html>
react hook 简易实现
最新推荐文章于 2024-03-19 08:15:00 发布