React useState 极简实现
useState 就是一个预置了 useReducer 的 useReducer
在内存中始终会有一个
fiber对象,采用链表的结构来保存, 每个useState都会产生一个fiber节点,当我们更新状态时,参与运算的是workInProgressHook, 它指向fiber节点的第一个hook,// update hook = workInProgressHook; workInProgressHook = workInProgressHook.next;通过上述代码,依次去执行对应
fiber更新,
循环链表的好处
-
任何节点都可以做为头节点。 可以从任何节点开始进行链表的遍历。只要当第一个节点被重复访问时,则意味着遍历结束。
-
用于实现队列数据结构是很有帮组的。 如果使用循环链表,则不需要为了队列而维护两个指针(front以及rear)。只需要维护尾节点一个指针即可,因为尾节点的后向节点就是front了。
-
循环链表常用于各应用程序中。 例如,当运行多个应用程序时,操作系统通常会把这些程序存入至一个链表,并进行循环遍历,给每个应用程序分配一定的时间来执行。此时循环链表对于OS是很有帮助的,当达到链表尾部时,可以方便的从头部重新开始遍历。
-
循环双向链表可以用于实现高级数据结构,例如斐波那契堆(Fibonacci Heap)。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body></body>
<script>
/**
* react中 我们可以通过current.fiber是否存在,来判断是mount 或 update
* 这里,我们用一个全局变量来进行判断 isMount
*/
let isMount = true;
let workInProgressHook = null; // 使用链表来保存 hooks, 参与计算的hook
const fiber = {
memoizedState: null, // 收集hook,组成链表
stateNode: App, // 保存当前函数组件
};
function dispatchAction(queue, action) {
const update = {
action,
next: null,
};
if (queue.pending === null) {
update.next = update;
} else {
update.next = queue.pending.next; // 当前的指向第一个
queue.pending.next = update;
}
// 指向最后一个action
queue.pending = update;
run();
}
function useState(initialState) {
let hook;
// 区分mount 和 update
if (isMount) {
// 创建hooks数据结构
hook = {
queue: {
pending: null,
},
// 保存了hooks对应的state
memoizedState: initialState,
// 指向下一个hook
next: null,
};
// mount时, 将hook 挂到 workInProgressHook 后面
if (!fiber.memoizedState) {
fiber.memoizedState = hook; // fiber -> hook 第一次
} else {
workInProgressHook.next = hook; // 1. workInPh.next -> hook 2. hook.next -> hook
}
workInProgressHook = hook; //
} else {
// update
hook = workInProgressHook;
workInProgressHook = workInProgressHook.next;
}
let baseState = hook.memoizedState;
// 此处进行更新操作, 基于baseState 进行更新
if (hook.queue.pending) {
let firstUpdate = hook.queue.pending.next; // 第一个update
do {
const action = firstUpdate.action;
baseState = action(baseState);
firstUpdate = firstUpdate.next; // 指向下一个update
} while (firstUpdate !== hook.queue.pending.next);
hook.queue.pending = null;
}
// 将计算完成的baseState 保存到memoizedState, 下次更新将以memoizedState为基础
hook.memoizedState = baseState;
// 传递 hook.queue, 就能在dispatchAction 取到pending
return [baseState, dispatchAction.bind(null, hook.queue)];
}
function App() {
const [num, setNum] = useState(1);
const [count, setCount] = useState(2);
// 调用useState方法,会执行state的计算过程,返回的updater方法 会执行updater的创建过程
console.log(num, count);
return {
onClick: () => {
setNum((num) => num + 1);
setCount((count) => count + 1);
},
setCount: () => {
setCount((count) => count + 1);
},
};
}
// 模拟 scheduker render commit 流程
function run() {
// hook的初始化操作,将fiber节点的第一个hook 赋值给参与计算的 workInProgressHook
workInProgressHook = fiber.memoizedState;
const app = fiber.stateNode(); // render阶段,执行函数组件
isMount = false;
return app;
}
window.app = run();
</script>
</html>
\