【前端面试】看react源码,解读useState

news2024/11/24 21:01:19

点击:react git 链接

截止2024.8.22最新版本如下在这里插入图片描述

React hooks

源码好深,hook封装位于packages/react-reconciler/src/ReactFiberHooks.js

hook的数据类型:

export type Hook = {
   
  memoizedState: any,
  baseState: any,
  baseQueue: Update<any, any> | null,
  queue: any,
  next: Hook | null,
};

HooksDispatcher

在 React 18 及更新的版本中,HooksDispatcher 是一个内部使用的调度器,它负责协调 React 组件的挂载(mount)和更新(update)过程中的 Hooks 调用。HooksDispatcherOnMountHooksDispatcherOnUpdateHooksDispatcher 在不同渲染阶段的实例,它们分别用于处理组件首次挂载和随后的更新。
HooksDispatcherOnMountHooksDispatcherOnUpdate 是 React 为了更好地控制组件渲染过程中 Hooks 的行为而引入的内部调度器实例。它们确保了 Hooks 在组件的不同生命周期阶段能够正确地执行。

HooksDispatcherOnMount

  • HooksDispatcherOnMount 是在组件首次挂载时使用的调度器实例。
  • 在这个阶段,React 会调用组件内的所有 Hooks,例如 useState, useEffect, useContext 等,并且是按照它们在代码中出现的顺序进行调用。
  • 由于是首次渲染,useState 会为每个 state 创建初始状态,useEffect 会执行所有 effect 的逻辑(但不会清除,因为没有之前的 effect)。
    在这里插入图片描述

HooksDispatcherOnUpdate

  • HooksDispatcherOnUpdate 是在组件更新时使用的调度器实例。
  • 在更新阶段,React 同样会调用组件内的所有 Hooks,但这次调用是有条件的。React 会根据调度器来判断 Hooks 是否需要被调用。
  • 例如,useState 会返回当前的状态值,useEffect 会根据 effect 的依赖项来决定是否执行 effect。
  • 更新阶段的 Hooks 调用通常涉及到比较前后的状态或 props,以确定是否需要执行某些操作或副作用。
    在这里插入图片描述

为什么需要不同的调度器实例?

  • 性能优化:通过在不同的渲染阶段使用不同的调度器实例,React 可以更精确地控制 Hooks 的行为,从而优化性能。
  • 避免副作用的重复执行:在更新阶段,React 需要区分哪些副作用需要重新执行,哪些可以保持不变。
  • 保持渲染的一致性:确保组件的渲染行为在不同的渲染阶段保持一致性。

useState解析

useState解析-mountState

mountState就是useState的实现,间接调用了mountStateImpl、mountWorkInProgressHook。下面展开详细解读

mountWorkInProgressHook

  • 拿到当前FiberNode的workInProgressHook变量(可以将其理解为hooks链表的指针),将其指向当前最新hook,并返回引用。
  • currentlyRenderingFiber.memoizedState指向hooks链表的头指针。

function mountWorkInProgressHook(): Hook {
   
  const hook: Hook = {
   
    memoizedState: null,

    baseState: null,
    baseQueue: null,
    queue: null,

    next: null,
  };

  if (workInProgressHook === null) {
   
    // This is the first hook in the list
    currentlyRenderingFiber.memoizedState = workInProgressHook = hook;
  } else {
   
    // Append to the end of the list
    workInProgressHook = workInProgressHook.next = hook;
  }
  return workInProgressHook;
}

通过调用,先将hook挂在到filber node的hooks链表上
计算传入的初始值,并赋值给hook.memoizedState
给hook.queue 赋初值。创建一个新的链表作为更新队列,用来存放更新(setXxx())

function mountStateImpl<S>(initialState: (() => S) | S): Hook {
   
  const hook = mountWorkInProgressHook();
  if (typeof initialState === 'function') {
   
    const initialStateInitializer = initialState;
    // $FlowFixMe[incompatible-use]: Flow doesn't like mixed types
    initialState = initialStateInitializer();
    if (shouldDoubleInvokeUserFnsInHooksDEV) {
      setIsStrictModeForDevtools(true);
      // $FlowFixMe[incompatible-use]: Flow doesn't like mixed types
      initialStateInitializer();
      setIsStrictModeForDevtools(false);
    }
  }
  hook.memoizedState = hook.baseState = initialState;
  const queue: UpdateQueue<S, BasicStateAction<S>> = {
   
    pending: null,
    lanes: NoLanes,
    dispatch: null,
    lastRenderedReducer: basicStateReducer,
    lastRenderedState: (initialState: any),
  };
  hook.queue = queue;
  return hook;
}

创建一个 dispatch 示例方法(即 useState 返回的数组的第二个参数:setXxx()),
该方法的作用是用来修改 state,并将此更新添加到更新队列中,通过 .bind 把当前 fiber node、更新队列、 dispatch 方法关联起来:


function mountState<S>(
  initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
   
  const hook = mountStateImpl(initialState);
  const queue = hook.queue;
  const dispatch: Dispatch<BasicStateAction<S>> = (dispatchSetState.bind(
    null,
    currentlyRenderingFiber,
    queue,
  ): any);
  queue.dispatch = dispatch;
  return [hook.memoizedState, dispatch];
}

综上,useState 在 Mount (组件初始化)阶段:

  1. 获取当前 Hook 节点,同时将当前 Hook 添加到 Hook 链表中
  2. 初始化 Hook 的状态,即读取初始 state 值
  3. 创建一个新的链表作为更新队列,用来存放更新操作setXxx(),

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2069058.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vue vue/cli3 与 vue/cli4 v-for 和 v-if 一起使用冲突

问题描述 异常信息&#xff1a;[vue/no-use-v-if-with-v-for] The this.$router.options.routers expression inside v-for directive should be replaced with a computed property that returns filtered array instead. You should not mix v-for with v-if.eslint-plugin-v…

基础算法--高精度数据(1)

高精度数据处理一般内容简单&#xff0c;写代码难度较大&#xff0c;可能部分内容涉及基础数学、初等数论等知识。请小心食用。不过本节不会给大家太难的高精度处理&#xff0c;我们第一次接触&#xff0c;不能劝退大家对吧。 高精度算法是指&#xff0c;利用基础或高级的数学…

pygame—炸弹牌(可做课设)

游戏介绍 在5X5的数字宫格里翻牌&#xff0c;翻出所有的2和3即可获胜每一格只能是0、1、2、3&#xff0c;第六列和最第六行为 X | Y&#xff0c;X代表该列或该行的数字总和&#xff0c;Y代表该列或该行的0的个数控制难度&#xff0c;每行每列的数字总和不超过9该游戏需要一定运…

Vue3学习笔记之数据绑定篇(0823)

学习完Vue2 的C友们&#xff0c;今天继续追赶Vue3的大潮流吧&#xff01; 废话不多说&#xff0c;直接上代码 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"…

MobaXterm接触session会话保存14个的限制

问题描述 在我们使用MobeXterm的过程中&#xff0c;发现session保存了14个之后&#xff0c;再无法继续保存了&#xff1b; 原因是免费版本的MobeXterm的最大个数被限制了&#xff0c;需要进行破解&#xff1b; MobaXterm-keygen解除session保存限制的python脚本 可以使用上面…

计算机的错误计算(七十一)

摘要 计算机的错误计算&#xff08;七十&#xff09;探讨了大数的正割函数的错误计算。本节讨论另外一类数值&#xff1a; 附近数 的正割函数的计算精度问题。 例1. 已知 计算 若用 在 Python下编程计算&#xff0c;则有 若在 Excel 中计算&#xff0c;则有&#xff1a…

Xmind 在线导图上线!多设备实时同步,节约本地空间

在现代职场上&#xff0c;高效的工作方法对于提升个人和团队的生产力至关重要。 Xmind 作为一款领先的思维导图软件&#xff0c;最近推出了其在线版本&#xff0c;旨在帮助我们解决在工作中常见的 「掉线状态」 问题&#xff0c;并提升工作效率。 在日常工作中&#xff0c;我们…

抖音如何去水印导出,3种高效工具让你轻松掌握

在抖音上&#xff0c;我们经常会遇到一些精彩视频想要保存下来&#xff0c;但视频上往往带有水印&#xff0c;影响了观看和分享的体验。下面&#xff0c;我将介绍三种去除抖音视频水印的方法&#xff0c;让你轻松保存无水印视频。 技巧一&#xff1a;奈斯水印助手(小程序) 这是…

基于大语言模型的物联网(artificial intelligence of thing)

与当下热门的AI类似&#xff0c;曾几何时&#xff0c;物联网&#xff08;Internet of thing&#xff09;实现“万物互联"给人类带来了无限的遐想。但是往往事与愿违&#xff0c;美好的愿景并没有如约而至。十几年来&#xff0c;物联网远没有实现”万物互联“的美好愿景。 …

Kafka·Producer

Producer发送原理 拦截器进行拦截 对key和value进行序列化 org.apache.kafka.clients.producer.KafkaProducer#doSend 分区选择 计算消息要发送到topic的哪个分区上 若指定了分区&#xff0c;则使用指定的值没有指定的话则使用分区器计算得到或者使用hash取余的方式 暂存…

Stm32通过SPI读写W25QXX

Printf的重定向 因为printf是c中的库函数&#xff0c;要使用printf输出到串口&#xff0c;需要重定向&#xff0c;将printf定向到HAL_UART_Transmit。 新建一个retarget.c文件。 #include "stdio.h" #include "stm32f1xx_hal.h" #include "usart.h&…

创意无限,尽在掌握:热门视频剪辑软件一览

我们记录生活、分享故事、传播信息用视频的频率越来越高了。而这些视频往往都是通过剪辑之后才能展示出当前的效果。那这次我们就来探索剪辑视频的时候都会用到什么工具吧。 1.福昕视频剪辑 连接直达>>https://www.pdf365.cn/foxit-clip/ 这是一款专为追求高效与创意…

Pytorch 张量运算函数(补充)

mean() mean()函数是进行张量均值计算的函数,常用参数可以设置参数dim来进行对应维度的均值计算 以下是使用一个二维张量进行演示的例子 import numpy as np import torch device torch.device(mps if torch.backends.mps.is_available() else cpu) print(device ) data1 …

【数据管理】数据治理

目录 1、相关概念 2、数据治理和管理职责语境关系图 3、业务驱动因素 4、目标和原则 5、 数据治理和数据管理的关系 6、数据治理组织 7、数据管理职能 8、数据制度 9、数据资产估值 1、相关概念 1&#xff09;战略(Stategy)&#xff1a;定义、交流和驱动数据战略和数…

[数据集][目标检测]电力场景输电线异物检测数据集VOC+YOLO格式2060张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;2060 标注数量(xml文件个数)&#xff1a;2060 标注数量(txt文件个数)&#xff1a;2060 标注…

电脑丢失dll文件一键修复之dll确实损坏影响电脑运行

在使用电脑过程中&#xff0c;DLL文件丢失或损坏是一个常见的问题&#xff0c;它可能导致程序无法正常运行&#xff0c;甚至影响整个系统的稳定性。本文将详细介绍如何一键修复丢失的DLL文件&#xff0c;探讨常见的DLL丢失报错原因&#xff0c;并提供详细的修复步骤和预防措施。…

sklearn回归树

说明&#xff1a;内容来自菜菜的sklearn机器学习和ai生成 回归树 调用对象的参数 class sklearn.tree.DecisionTreeRegressor (criterion’mse’, splitter’best’, max_depthNone, min_samples_split2, min_samples_leaf1, min_weight_fraction_leaf0.0, max_featuresNone…

大数据基础:数仓架构演变

文章目录 数仓架构演变 一、传统离线大数据架构 二、​​​​​​Lambda架构 三、Kappa架构 四、​​​​​​​​​​​​​​混合架构 五、湖仓一体架构 六、流批一体架构 数仓架构演变 20世纪70年代&#xff0c;MIT(麻省理工)的研究员致力于研究一种优化的技术架构&…

Linux shell编程学习笔记75:sed命令——沧海横流任我行(下)

0 前言 在 Linux shell编程学习笔记73&#xff1a;sed命令——沧海横流任我行&#xff08;上&#xff09;-CSDN博客文章浏览阅读684次&#xff0c;点赞32次&#xff0c;收藏24次。在大数据时代&#xff0c;我们要面对大量数据&#xff0c;有时需要对数据进行替换、删除、新增、…

OpenCV几何图像变换(9)仿射变换函数warpAffine()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 函数是应用一个仿射变换到图像上。 warpAffine 函数使用指定的矩阵对源图像进行仿射变换&#xff1a; dst ( x , y ) src ( M 11 x M 12 y M…