react hook 简易实现

摘自:自顶向下学 React 源码

<!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>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值