zoukankan      html  css  js  c++  java
  • React Hooks 的存取原理

    每个组件的 fiber 上都有个 memorizedState 属性用于存储这个组件的所有 hooks。hooks 中的每个 hook 也有个 memorizedState 用于存储这个 hook 的数据。而每个 hook 还有个 next 指向下一个 hook。
    这里我们用链表实现 hooks 的存储,也可以用数组实现,只要有顺序就行。

    // 数据结构示例
    fiber = {
        // fiber 的 memorizedState 用于存储此组件的所有 hooks
        // 在链表的 hooks 实现中就是指向第一个 useXxx 生成的 hook;数组实现中就是一个数组,第一个 hook 存储在索引0中。
        memorizedState: hook1 {  // 第一个 useXxx 生成的 hook
            // useXxx 的数据
            memorizedState: data,
            // next 是个指针,指向下一个 useXxx 生成的 hook
            next: hook2 {
                // hook2 的数据
                memorizedState: data,
                // next 指向第三个 hook
                next: hook3
            }
        }
    }
    

    mount 阶段创建 hook

    在组件的 mount 阶段,当调用 useState, useEffect, useMemo 等 useXxx hooks 时都会创建一个 hook 对象,并用 memorizedState 属性保存数据,还有个 next 属性用于指向下一个 hook,不同的 hook 还有一些其它不同的属性,主要是这两个。然后这个 hook 会添加到当前组件的 fiber 上,即 fiber.memorizedState 中。多次调用 useXxx 的时候,每次都创建一个 hook,新的 hook 会与上一个 hook 的 next 建立连接。hook1.next 为 hook2,hook2.next 为 hook3。

    // 指向 hook 的指针
    let workInProgressHook = null;
    if (isMount) {
        // useState, useEffect, useRef 这些 hooks 都是创建一个 hook 对象,然后用 memorizedState 存储 hook 的数据
        hook = {
            memorizedState: initState,  // 当前 hook 数据
            next: null,  // 指向下一个 hook 的指针
        }
        if (!fiber.memorizedState) {
            fiber.memorizedState = hook;  // 不存在则是第一调用 useXxx,将 fiber.memorizedState 指向这第一个 hook
        } else {
            // fiber.memorizedState 存在则是多次调用 useXxx,将上个 hook 的 next 指向当前 hook
            workInProgressHook.next = hook;
        }
        workInProgressHook = hook;  // 存储当前 hook 用于下次使用
    } else {
        hook = workInProgressHook;
        workInProgressHook = hook.next;
    }
    // 使用 hook 执行的其它逻辑...
    

    update 阶段更新 hook

    而在组件更新的时候,比如函数组件也会按着代码流程依次执行,当遇到第一个 useXxx 的时候我们就可以从当前组件的 fiber.memorizedState 上取出第一个 hook,那么这个 hook 就是这个 useXxx 在 mount 阶段创建的 hook;遇到第二个 useXxx 就会从 fiber.memorizedState 上取出第二个 hook;以此类推,这是一一对应的关系。

    // update 阶段,每个 useXxx 被调用的时候都会走 else 逻辑
    } else {
        // workInProgressHook 是从第一个 hook 开始的,因为更新是通过 scheduler 来更新的,
        // 而 scheduler 中对 workInProgressHook 进行了复位操作,即 workInProgressHook = fiber.memorizedState
        hook = workInProgressHook;
        // workInProgressHook 指向下一个 hook
        workInProgressHook = hook.next;
    }
    // 使用 hook 执行的其它逻辑...
    

    明白了上面的逻辑我们就知道为什么不能将 useXxx 写在 if 等条件判断里了,因为这样就会导致 fiber.memorizedState 上存储的 hook 顺序错乱。

  • 相关阅读:
    00027_方法的重载
    Creating a Physical Standby Database 11g
    APUE信号-程序汇总
    随手记Swift基础和Optional Type(问号?和感叹号!)
    双十二即将来袭!阿里内部高并发系统设计手册终开源,你那系统能抗住“秒杀”吗?
    ajax初见
    编程基本功:BUG测试步骤尽可能用文档简化,突出重点
    年轻就该多尝试,教你20小时Get一项新技能
    微信小程序-封装请求基准路径、接口API 和使用
    理解Python闭包,这应该是最好的例子
  • 原文地址:https://www.cnblogs.com/3body/p/15431418.html
Copyright © 2011-2022 走看看