React16源码: React中context-stack的源码实现

context-stack


1 ) 概述

  • 在 context 内部有一个概念是 stack
    • 有一个唯一的stack
    • 里面保存各种各样的东西
  • stack的特性
    • 在更新节点的时候,会把相关的信息入栈
      • 在因为stack就是栈,在里面会存储各种各样的信息
      • 在更新节点的时候,每一个节点的信息都会推入这个stack
    • 完成节点更新的时候,相关的信息需要出栈
      • 因为栈是一个先入后出的这么一个数据结构
      • 这正好对应于 react的更新过程中的 beginWork 是从头到尾
      • 沿着子树的一侧这么下来的一个过程,这个时候我们从头到尾,推入了相关的信息
      • 然后在 completeUnitOfWork 的时候,我们又是从尾到头去进行一个完成节点更新的操作
      • 这个时候刚好是一个相反的顺序
      • 在这个相反的顺序当中,又可以同时出栈,这样的话, 就可以保证我们的栈从更新开始到最终更新结束
      • 因为从更新开始到更新结束,都是要回到 root 节点,它这个栈更新开始的时候是空的
      • 更新结束的时候,也刚好变成一个空的状态
      • 这就是在更新时入栈,在完成节点时出栈
    • 在这个栈的数据结构中可以用不同的cursor来记录不同的信息
      • react的这个stack的实现过程中,它需要存的信息是非常多的
      • 比如说有新的 contextAPI 也有老的 contextAPI
      • 还有跟 HostComponent 相关的一些信息,还有container相关的一些信息
      • 这些信息之间是互不干涉,没有任何关联的,这些信息都要存在同一个stack里面
      • 通过 cursor 去标记这些相关性的信息自己处于哪个位置

2 )源码

定位到 packages/react-reconciler/src/ReactFiberStack.js

/*** Copyright (c) Facebook, Inc. and its affiliates.** This source code is licensed under the MIT license found in the* LICENSE file in the root directory of this source tree.** @flow*/import type {Fiber} from './ReactFiber';import warningWithoutStack from 'shared/warningWithoutStack';export type StackCursor<T> = {current: T,
};const valueStack: Array<any> = [];let fiberStack: Array<Fiber | null>;if (__DEV__) {fiberStack = [];
}let index = -1;function createCursor<T>(defaultValue: T): StackCursor<T> {return {current: defaultValue,};
}function isEmpty(): boolean {return index === -1;
}function pop<T>(cursor: StackCursor<T>, fiber: Fiber): void {if (index < 0) {if (__DEV__) {warningWithoutStack(false, 'Unexpected pop.');}return;}if (__DEV__) {if (fiber !== fiberStack[index]) {warningWithoutStack(false, 'Unexpected Fiber popped.');}}cursor.current = valueStack[index];valueStack[index] = null;if (__DEV__) {fiberStack[index] = null;}index--;
}function push<T>(cursor: StackCursor<T>, value: T, fiber: Fiber): void {index++;valueStack[index] = cursor.current;if (__DEV__) {fiberStack[index] = fiber;}cursor.current = value;
}function checkThatStackIsEmpty() {if (__DEV__) {if (index !== -1) {warningWithoutStack(false,'Expected an empty stack. Something was not reset properly.',);}}
}function resetStackAfterFatalErrorInDev() {if (__DEV__) {index = -1;valueStack.length = 0;fiberStack.length = 0;}
}export {createCursor,isEmpty,pop,push,// DEV only:checkThatStackIsEmpty,resetStackAfterFatalErrorInDev,
};
  • 首先定义一个非常重要的变量叫做 valueStack,它是一个数组
  • 接下去这边有一个 fiberStack,它只是用于开发的过程当中, 可以先忽略它
  • 定义一个全局 index
    • 注意这个 index 就是对应 valueStack 目前存数据存在的哪个位置
  • 接下去, 声明了一个方法叫做 createCursor
    • 直接 return 了一个新的对象
    • 这个对象里面有一个 current 属性
    • 在创建的时候传入的一个 defaultValue 给 current 赋值
    • 它是一个 pure 的对象,没有任何其他的一些特性
  • 注意
    • 这整个文件,它对应的是一个模块
    • valueStack 定义一些什么东西
    • 它里面的所有的数据都是有关联性的
    • 主要的数据也就是 valuestack 跟 index 是在这个模块里面
    • 它其他的数据都是在外部,使用的时候才会创建的
    • 这个 index 它本来是标记 valueStack 对应数据存在哪个位置
    • 可以通过它是否等于 -1 来判断我们目前的 valueStack 它是否是一个空的数组
  • 接下去的 pop 方法
    • 如果 index < 0
      • 说明 valueStack 里面没有任何的数据,直接return
      • 因为我们没有任何东西是可以推出来的
    • 读取 index 在 valueStack 对应的 value 存入 cursor.current
      • 因为在推入的时候,就是把老的值存到 valueStack上面,然后新的值它等于新设置的值
      • 如果要 pop 的时候,对应的肯定要把老的那个值再赋值回来
    • 将 index 对应在 valueStack 中的 value 置空
    • 将 index –
  • 接下去的 push 方法
    • 它接收的参数 cursor, value, fiber
    • cursor 是通过 createCursor 这个方法来创建的一个游标对象
    • 将 index ++
    • 将老的 value (cursor.current) 推入到 栈 当中
    • 将当前 cursor.current 指向传进来的 value
    • 注意,在一次 push 中新的 value 不会入栈的
  • checkThatStackIsEmpty
    • 这个只有在开发时会用到,其实就跟 isEmpty 是一样的
    • 它判断一下index是否等于 -1,然后进行一个warning
  • resetStackAfterFatalErrorInDev
    • dev 相关变量,无需过多关注
    • 就是重置了一些值
  • 注意
    • 如果在对 valueStack 进行操作的过程中,顺序是不一样的
    • 比如说,我有三种不同的数据类型,就有三个不同的cursor
    • 3个不同的 cursor 它们推入的这些数据,保存的不一样
    • 比如说,我目前这个数组先推入了一个a,然后再推入了一个b,然后再推入了一个c
    • 它们分别对应的是 cursor1(c1), cursor2(c2), cursor3(c3),它们想要读取的数据应该是这样的
    • 即 [a, b, c] => c1, c2, c3
    • 在 pop 的时候,可能先 pop 了 c1,但是我拿到的是却是 c3 对应的数据 c
    • 这个就是我们不想要出现的一个情况,实际上这个文件内没有解决这个问题
    • 因为react在更新的过程当中,先从 beginWork 里面推数据
    • 然后在 completeUnitOfWork 里面去提出数据
    • 这个过程中,它始终保持着 cursor 的一一对应关系
    • 也就是说我推进来的是c1, c2, c3的顺序
    • 推出的时候,要先推出 c3,再推出 c2,再推出 c1
    • 它们拿到的值都是一个一一对应的关系
    • 这是react在使用这个stack模块的时候去控制的
    • 而不是在我们这个模块内部去解的这个问题
    • 比如说 context 它一个入栈,出栈的一个情况的时候就会有这个调用的过程
    • 这个目前先跳过

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

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

相关文章

可曾听闻Nginx?

目录 一.简介 二.Nginx的优点 三.反向代理 四.负载均衡 五.动静分离 六.总结 说起Nginx&#xff0c;耳边好像经常听到&#xff0c;但是不知道用来干嘛的。最近用到了&#xff0c;所以我就大概总结了基本知识&#xff0c;它经常与一个词出现在一起——反向代理。那接下来…

配置华为交换机环路检测案例

知识改变命运&#xff0c;技术就是要分享&#xff0c;有问题随时联系&#xff0c;免费答疑&#xff0c;欢迎联系&#xff01; 厦门微思网络​​​​​​https://www.xmws.cn 华为认证\华为HCIA-Datacom\华为HCIP-Datacom\华为HCIE-Datacom 思科认证\CCNA\CCNP\CCIE Linux\RHCE…

YOLOv5可视化热力图 | 支持自定义模型、置信度选择等功能(论文必备)

一、本文介绍 本文给大家带来的机制是的可视化热力图功能,热力图作为我们论文当中的必备一环,可以展示出我们呈现机制的有效性,本文的内容支持YOLOv5最新版本,同时支持视频讲解,本文的内容是根据检测头的输出内容,然后来绘图。 在开始之前给大家推荐一下我的专栏,本专…

Vue服务器端渲染(SSR)是不是技术的倒退?

一、什么是服务器端渲染&#xff0c;是不是技术退步&#xff1f; Vue服务器端渲染&#xff08;Vue Server-Side Rendering&#xff0c;简称SSR&#xff09;是一种将Vue组件在服务器端进行渲染&#xff0c;生成最终的HTML页面&#xff0c;然后将其发送给客户端的技术。 传统的V…

烧录软件(Renesas Flash Programmer)瑞萨RL78G12系列单片机下载工具(E2)的软件配置与硬件链接说明

一、单片机与仿真器连接 E1引脚接线图 RL78系列单片机的GND接仿真器的pin2、pin12、pin14 RL78系列单片机的VDD接仿真器的pin8 RL78系列单片机的Tool0接仿真器的pin5 RL78系列单片机的Reset接仿真器的pin10、pin13 二、确认接线完成后&#xff0c;开始烧录 1、打开RFPV软件…

C 练习实例50-使用Dev-C++创建项目(圆形体体积计算器)

项目展示 项目案例&#xff1a;圆形体体积计算器 vol.h文件 #include <stdio.h> #include <math.h> #define PI 3.141592654 void cal(int sel); //函数声明 double vol_ball(void); double vol_cylind(void); double vol_cone(void); main.c文件 #include &quo…

【RT-DETR有效改进】手把手带你调参RT-DETR,复现官方版本实验环境

👑欢迎大家订阅本专栏,一起学习RT-DETR👑 一、本文介绍 本文是带大家进行调参,利用ultralytics仓库1:1复现RT-DETR官方实验环境,从而在后期发表论文 的时候省去一些不必要的麻烦,例如被审稿人提出质疑,本文的调参内容均有依据,根据RT-DETR官方Github上发布的版…

【数字通信】数字带通传输

数字调制和数字带通传输系统 数字调制解调 数字调制 用数字基带信号控制载波&#xff0c;把数字基带信号变换为数字带通信号的过程 目的&#xff1a;数字基带信号含大量低频分量&#xff0c;无法通过具有带通特性的信道传输。需对数字基带信号进行数字调制使信号与信道的特…

网际协议 IP、IP地址

目录 网际协议 IP 虚拟互连网络 使用中间设备进行互连 IP 地址 IP 地址及其表示方法 ​编辑 IP 地址采用 2 级结构 IP 地址的编址方法 分类的 IP 地址 各类 IP 地址的指派范围 一般不使用的特殊的 IP 地址 IPv4网络中的地址类型 分类的 IP 地址的优点和缺点 划分子网…

《zdppy_aocrud官方教程》 02 自动生成根据ID查询接口

02 自动生成根据ID查询接口 《zdppy_aocrud官方教程》 02 自动生成根据ID查询接口 自动生成根据ID查询用户的方法 服务端 import aocrud import env import orm from api import Api, respenv.load("../.env")db, BaseModel orm.get_mysql_pool_base_model_by_e…

数字媒体技术基础之:声波及其测量

声音始于空气中的振动&#xff0c;如吉他弦、人的声带或扬声器纸盆产生的振动。 这些振动一起推动邻近的空气分子&#xff0c;从而轻微增加空气压力。 压力下的空气分子随后推动周围的空气分子&#xff0c;后者又推动下一组分子&#xff0c;依此类推。 高压区域穿过空气时&…

链表中倒数第k个结点

题目描述 输入一个链表&#xff0c;输出该链表中倒数第k个结点。 这道题首先按照普通的方法用一个栈来存放链表数据然后在取出第k的数就可以了,但是这种方法的时间复杂度是O(n),不提倡; 现在我介绍一种时间复杂度为O(n-k)的方法,老规矩-->先贴源码,在分析: /*public clas…

appsmith安装手记:3.appsmith安装

终于到了安装appsmith这个大神的时候&#xff0c;这个简单&#xff0c;真是越到后面越容易。 安装说明见&#xff1a;https://docs.appsmith.com/getting-started/setup/installation-guides/docker &#xff0c;建议使用 Docker Compose 进行长期的 Appsmith 部署 步骤一&…

SpringSecurity笔记

SpringSecurity 本笔记来自三更草堂&#xff1a;https://www.bilibili.com/video/BV1mm4y1X7Hc/?spm_id_from333.337.search-card.all.click&#xff0c;仅供个人学习使用 简介 Spring Security是Spring家族中的一个安全管理框架。相比与另外一个安全框架Shiro&#xff0c;…

Leetcode刷题笔记题解(C++):1117. H2O 生成(多线程)

思路&#xff1a; 解法二&#xff1a;生产者-消费者解法 1.把 hydrogen 线程看作生产者&#xff0c;oxygen 线程看作消费者&#xff0c;缓冲队列大小为2。 2.hydrogen 把生成的氢放入队列&#xff1b;oxygen 线程每次从队列里消费两个氢元素。 3.生产者生产两个氢元素后会因为…

Selenium解决滑块验证问题:自动化与技巧

滑块验证是一种常见的人机验证机制&#xff0c;用于识别和防止自动化程序的访问。在爬虫和自动化测试中&#xff0c;我们经常面临需要处理滑块验证的情况。本文将介绍如何使用 Selenium 库处理滑块验证问题。 一、安装 Selenium 首先&#xff0c;确保你已经安装了 Selenium。你…

【极数系列】Linux环境搭建Flink1.18版本 (03)

文章目录 引言01 Linux部署JDK11版本1.下载Linux版本的JDK112.创建目录3.上传并解压4.配置环境变量5.刷新环境变量6.检查jdk安装是否成功 02 Linux部署Flink1.18.0版本1.下载Flink1.18.0版本包2.上传压缩包到服务器3.修改flink-config.yaml配置4.启动服务5.浏览器访问6.停止服务…

[go] 享元模式

享元模式 是一种结构型设计模式&#xff0c; 它摒弃了在每个对象中保存所有数据的方式&#xff0c; 通过共享多个对象所共有的相同状态&#xff0c; 让你能在有限的内存容量中载入更多对象。 模型说明 享元模式只是一种优化。在应用该模式之前&#xff0c;你要确定程序中存在…

二叉树--199. 二叉树的右视图/medium 理解度C

199. 二叉树的右视图 1、题目2、题目分析3、复杂度最优解代码示例4、适用场景 1、题目 给定一个二叉树的 根节点 root&#xff0c;想象自己站在它的右侧&#xff0c;按照从顶部到底部的顺序&#xff0c;返回从右侧所能看到的节点值。 示例 1: 输入: [1,2,3,null,5,null,4] 输出…

贪吃蛇项目(基于C语言和数据结构中的链表)

建立文件 首先先建立3个文件。 Snake.h 函数的声明 Snake.c 函数的定义 Test.c 贪吃蛇的测试 分析项目 我们分析这整个项目 建立节点 首先在我们实现游戏开始的部分之前&#xff0c;我们要先创建贪吃蛇的节点&#xff0c;再由此创建整个贪吃蛇所包含的一些信息&#…