LEARN_框架_React-Hook原理解析

useState / useReducer

useState 和 useReducer 都是关于状态值的提取和更新,从本质上来说没有区别,从实现上看,useState 是 useReducer 的一个简化版,其背后用的是同一套逻辑。

React Hook 是如何保存状态

React 官方文档中提到,React Hook 保存状态的位置与类组件是一致的,但其实略有差别:

  • 两者的状态值都被挂载在组件实例对象 FiberNode 的 memoizedState 属性中
  • 两者保存状态值的数据结构完全不同。类组件是直接把 state 属性挂载在这个开发者自定义的对象保存到 memorizedState 属性中;而 Hook 是用链表来保存状态,memorizedState 属性保存的实际是这个链表的头指针

我们来看看这个链表节点是什么样子的:

// react-reconciler/src/ReactFiberHooks.js
export type Hook = {
  memoizedState: any, // 最新的状态值
  baseState: any, // 初始状态值,如`useState(0)`,则初始值为0
  baseUpdate: Update<any, any> | null,
  queue: UpdateQueue<any, any> | null, // 临时保存对状态值的操作,更准确来说是一个链表数据结构中的一个指针
  next: Hook | null,  // 指向下一个链表节点
};

官方文档一直强调 React Hooks 的调用只能放在函数组件/自定义 Hooks 函数体的顶层,这是因为我们只能通过 Hooks 调用的顺序来与实际保存的数据结构来关联:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MSpfqylx-1629268031158)(https://user-gold-cdn.xitu.io/2020/7/1/17307e7bb3014026?imageslim)]

虽然上面一致都是以 useState 和 useReducer 来作为例子说明,但实际上所有 React Hooks 都是用这种链表的方式来保存的。

React Hook 是如何更新状态

熟悉 Hook API 的话,我们都知道如何去更新状态,那么由 useState 返回的这个用来更新状态的函数(下文统称为 dispatcher)的运行原理是什么?

当我们每次调用 dispatcher 时,并不会立刻对状态值进行修改(对的,状态值的更新是异步的),而是创建一条修改操作–在对应 Hook 对象的 queue 属性挂载的链表上加一个新的节点:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RG7hs4CA-1629268031161)(https://user-gold-cdn.xitu.io/2020/7/1/17307e7f74c14584?imageslim)]

在下次执行函数组件,再次调用 useState 时,React 才会根据每个 Hook 上挂载的更新操作链表来计算最新的状态值。为什么需要把每次更新操作都记录下来,那是因为 Hook 支持这样的操作:

const [name, setName] = useState('')
setName(name => name + 'a')
setName(name => name + 'b')
setName(name => name + 'c')

// 下次执行时就可以得到 name 的最新状态值为'abc'啦

useEffect

useEffect 的保存方式和 useState / useReducer 类似,也是以链表的形式挂载在 FiberNode.updateQueue 中。

下面我们按照 mount 和 update 这两个组件的生命周期来阐述 useEffect 的执行原理:

mount 阶段:mountEffect

  1. 根据函数组件函数体中依次调用 useEffect 语句,构成一个链表并挂载在 FiberNode.updateQueue 中,链表节点的数据结构为:

    const effect: Effect = {
        tag, // 用来标识依赖项有没有变动
        create, // 用户使用useEffect传入的函数体
        destroy, // 上述函数体执行后生成的用来清除副作用的函数
        deps, // 依赖项列表
        next: (null: any),
    };
    
  2. 组件渲染完成后,遍历链表执行

update 阶段:updateEffect

初始阶段

同样在依次调用 useEffect 语句 时,判断此时传入的依赖列表,与链表节点 Effect.deps 中保存的是否一致(基本数据类型的值是否相同,对象的引用是否相同),如果一致,则在 Effect.tag 标记上 NoHookEffect

执行阶段

在每次组件渲染完成后,就会进入 useEffect 的执行阶段 function commitHookEffectList()

  1. 遍历链表
  2. 如果遇到 Effect.tag 被标记上 NoHookEffect 的节点则跳过
  3. 如果 Effect.destroy 为函数类型,则需要执行该清除副作用的函数
  4. 执行 Effect.create,并将执行结果保存到 Effect.destroy(如果开发者没有配置 return,那么得到的自然是 undefined,也就是认为开发者对当前的 useEffect 没有需要清除的副作用);注意由于闭包的缘故,Effect.destory 实际上可以访问到本次 Effect.create 函数作用域的变量

我们注意到的一点是:先清除上一轮的 effect,然后再执行本轮的 effect

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
React Typewriter Hook是一个使用React Hooks来实现打字机效果的库。它可以帮助我们在React应用中轻松地实现打字机效果。 要使用React Typewriter Hook,我们首先需要通过npm来安装它,命令如下:npm install react-typewriter-hook。 使用React Typewriter Hook的核心代码包括引入React和useTypewriter钩子,并在组件中使用useTypewriter钩子来创建打字效果。例如,在一个名为Note的组件中,我们可以通过useTypewriter钩子传入一个字符串来实现打字效果。 具体使用React Typewriter Hook的例子可以在官方文档中查看。在使用时,我们只需要根据示例的用法将useTypewriter钩子应用在需要实现打字效果的地方即可。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [【分享创造】react-typewriter-hook: 用react hooks来实现打字机的效果](https://blog.csdn.net/weixin_33924312/article/details/88613110)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [React引入react-typewriter-hook实现打字效果](https://blog.csdn.net/AK852369/article/details/119819250)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [react-typewriter-hook:Use使用react钩子轻松输入效果](https://download.csdn.net/download/weixin_42131276/18402977)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值