极简hooks实现,与react的思路一样的useState
Posted lin-fighting
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了极简hooks实现,与react的思路一样的useState相关的知识,希望对你有一定的参考价值。
使用App函数模拟根组件
function App()
const [num, updateNum] = useState(0);
console.log(`$isMount ? "mount" : "update" num: `, num);
button.textContent = num;
return
click()
updateNum((num) => num + 1);
updateNum((num) => num + 1);
,
;
使用app().click模拟点击事件。
全局变量
let workInProgressHook; //指向当前的hooks对象
let isMount = true; //判断当前是mount还是update。
App对应的fiber
// 状态保存在函数组件的fiber上
// App组件对应的fiber对象
const fiber =
// 保存该FunctionComponent对应的Hooks链表
memoizedState: null,
// 指向App函数
stateNode: App,
;
useState函数
每个hooks会创建一个hooks对象,然后存放在fiber的memoizedState上形成链表。workInProgressHook就指向当前的hooks对象。
function useState(initialState)
//每个hook对应一个hooks对象
let hook;
if (isMount)
// ...mount时需要生成hook对象
hook =
queue:
pending: null,
,
memoizedState: initialState, //当前hooks对应的状态
next: null, //指向下一个hooks对象
;
//判断fiber上的链表
if (!fiber.memoizedState)
fiber.memoizedState = hook;
else
workInProgressHook.next = hook; //连接到fiber的hooks链表上
workInProgressHook = hook; //workInProgressHook指向最新的hook对象
else
// update的时候,从workInProgressHook中取出该useState对应的hook
hook = workInProgressHook;
// 指针往下移动
workInProgressHook = workInProgressHook.next;
let baseState = hook.memoizedState;
let newState = baseState;
if (hook.queue.pending)
// ...根据queue.pending中保存的update更新state
// 获取第一个update对象。pending存放着update对象的环状链表,指向的永远是最后一个update
let firstUpdate = hook.queue.pending.next;
do
//执行update action
const action = firstUpdate.action; // 假设所有的setState(()=>)都传递了一个函数
newState = action(baseState); //每次传入的baseState都是老值
firstUpdate = firstUpdate.next;
//最后一个update执行完毕后跳出循环。
while (firstUpdate !== hook.queue.pending.next);
// 清空queue.pending
hook.queue.pending = null;
// 将更新后的state重新赋值hooks对象上。
hook.memoizedState = newState || baseState;
// dispatchAction的第一个参数就是当前hooks对象的queue
return [newState, dispatchAction.bind(null, hook.queue)];
如上,setState是dispatchAction.bind(null, hook.queue),他的实现是:
每一个任务更新都会创建一个update对象,他与hooks对象可不同,update对象会通过next指针形成链表,存放在hooks对象queue.pending中,表示需要更新的操作。
// 调用updateNum实际上调用这个,queue就是当前hooks对应的queue。
function dispatchAction(queue, action)
// 每一个任务对应一个update
const update =
// 更新执行的函数
action,
// 与同一个Hook的其他更新形成链表
next: null,
;
// 环状单向链表操作
if (queue.pending === null)
update.next = update;
// 连向自己
else
// 形成环状
update.next = queue.pending.next;
queue.pending.next = update;
// 将更新的任务保存在hooks上的queue,下次组件执行的时候获取然后更改state
queue.pending = update; //永远指向最新的update
// 模拟React开始调度更新
schedule();
最后每次调用完setState,都会进入schedule调度阶段。
// 模拟react调度,默认执行一次
function schedule()
workInProgressHook = fiber.memoizedState;
const app = fiber.stateNode(); //执行App函数
isMount = false;
return app;
每次调度都会重新执行App函数,然后App函数就会执行useState。然后取出存放在hooks.queue中需要更新的action,执行完后获取最新的state并且返回。
模拟试试
至此,这就是整个hooks的执行过程。
总结:
每个组件对应着一个fiber节点,每个hook对应一个hooks对象,hooks对象存放在fiiber节点上,而每次执行更新函数,会创建一个update,他通过next指针形成链表,存放在hooks对象上的queue.pending上,是一个环状链表,然后执行调度。调度会重新执行函数组件,然后重新执行useState函数,他会获取存放在hooks对象上的queue上的update链表,然后取出之后执行,获取新的状态state返回。
函数组件的状态存放在hooks对象上的memoizedState上,更新的action存放在hooks对象上queue的pending上,而hooks对象存放在fiber节点上。
与React的区别
我们用尽可能少的代码模拟了Hooks的运行,但是相比React Hooks,他还有很多不足。以下是他与React Hooks的区别:
React Hooks没有使用isMount变量,而是在不同时机使用不同的dispatcher。换言之,mount时的useState与update时的useState不是同一个函数。
React Hooks有中途跳过更新的优化手段。
React Hooks有batchedUpdates,当在click中触发三次updateNum,精简React会触发三次更新,而React只会触发一次。
React Hooks的update有优先级概念,可以跳过不高优先的update。
更多的细节,我们会在本章后续小节讲解。
学习文章出自:https://react.iamkasong.com/hooks/create.html#%E5%9C%A8%E7%BA%BFdemo
以上是关于极简hooks实现,与react的思路一样的useState的主要内容,如果未能解决你的问题,请参考以下文章
[React] Use the React Effect Hook in Function Components
[React + GraphQL] Use useLazyQuery to manually execute a query with Apollo React Hooks