React16源码: Hooks源码实现
Hooks
1 )概述
- Hooks 在 React16.7版本出现的新功能
- Hooks 改变了整体应用开发的模式,同时开发体验会和以前会变得不一样
- Hooks 让函数组件具有类组件的能力
- 在 function component 里面没有this无法保存 state
- 通过 Hooks可以让 function component 代替 class component
- Hooks 让函数组件变得更强大
2 )用例演示
import React, { useState, useEffect } from 'react'export default () => {const [name, setName] = useState('Wang')useEffect(() => {console.log('component update')return () => {console.log('unbind')}}, [])return (<><p>My Name is: {name}</p><input type="text" value={name} onChange={e => setName(e.target.value)} /></>)
}
- 这里声明了一个functional component ,在以前对比class component它缺少的是什么
- 缺少就是this对象,它没有this对象,那么它就不能有 this.state
- 它就具有包含自己本身的状态的这么一个功能
- 它没有生命周期方法
- 在这里面我们使用了hooks来给我们的组件去存储了 state
- 使用
useState
传入了一个默认值是 Wang - 然后它返回一个数组,这是一个数组的解构
- 这个数组第一项是state的对应的这个变量
- 第二项是让我们去改变这个state的方法
- 这就是我们通过useState返回给我们的唯一的两个东西
- 使用
- 然后我们就可以在我们渲染的过程当中去使用这个state了,同样可以去修改这个state
- 比如说我们绑定了这个input的 onchange 事件
- 就是去修改这个state,就是我们的name
- 输入的内容之后它的state就自动更新
- 在下一次渲染的时候能够拿到 state
- 这就是hooks,它给 function component 提供了class component 所具有的能力
- 它的意义不仅仅是为了替代 class compoment
- 是想要去帮助我们去拆分一些在组件内部的逻辑
- 把他们提取出来,能够给更多的组件进行一个复用
- 以前在class compoment 里面是很难去拆分这部分逻辑的
- 还有一个是跟 class component 的最大区别,就是生命周期方法
- 在function component 里面,使用hooks可以通过一个叫做
useEffect
的这个API - 这个东西呢我们就可以传入一个方法,这个方法里面
- 比如说,随便写一句 component updated
- 在hooks里面,他没有着重的去区分 mounted 和 updated
- 它的理念是 mounted和updated 都是 updated
- 每一次有组件内容更新的时候都会去调用我们传入的这个 effect 回调函数
- 在function component 里面,使用hooks可以通过一个叫做
- 如果需要事件绑定什么的,之前在unmount的时候,去解除事件绑定,那这时怎么办
- 很简单,看到上面 return一个方法,这个方法就是解除我们的绑定,这边叫做 unbind
- 在目前这种情况下,每一次有更新的时候都会先执行unbind,然后再重新bind
- 这是比较符合react更新的一个逻辑的,就是在它有任何更新的时候
- 都会把之前的状态全部消除,然后返回新的状态
- 当然这对于一些事件监听的绑定不是特别友好, 解决方案如下
- 把它改造成行为,类似于 componentDidMount 和 componentWillUnmount
- 直接在这个 useEffect 传入第二个参数,然后传一个空数组
- 比如说传入一个props来进行一个它是否有变化的一个区分
- 如果传一个空数组,它就没有东西区分,就代表着只要执行一次
- 这样,在输入改变state时,就没有了,只再刷新后,才会执行一次
- 同样,在跳出的时候打印输出了 unbind
- 这就是我们使用 hooks 模拟生命周期方法的一个用法
3 ) 源码分析
这个要在 React 16.7 版本上来找,在 React.js 中可看到
import {useCallback,// ... 很多 khoos
} from './ReactHooks';
现在定位到 ReactHooks.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 {MutableSource,MutableSourceGetSnapshotFn,MutableSourceSubscribeFn,ReactContext,
} from 'shared/ReactTypes';
import type {OpaqueIDType} from 'react-reconciler/src/ReactFiberHostConfig';import invariant from 'shared/invariant';import ReactCurrentDispatcher from './ReactCurrentDispatcher';type BasicStateAction<S> = (S => S) | S;
type Dispatch<A> = A => void;function resolveDispatcher() {const dispatcher = ReactCurrentDispatcher.current;invariant(dispatcher !== null,'Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for' +' one of the following reasons:\n' +'1. You might have mismatching versions of React and the renderer (such as React DOM)\n' +'2. You might be breaking the Rules of Hooks\n' +'3. You might have more than one copy of React in the same app\n' +'See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.',);return dispatcher;
}export function useContext<T>(Context: ReactContext<T>,unstable_observedBits: number | boolean | void,
): T {const dispatcher = resolveDispatcher();if (__DEV__) {if (unstable_observedBits !== undefined) {console.error('useContext() second argument is reserved for future ' +'use in React. Passing it is not supported. ' +'You passed: %s.%s',unstable_observedBits,typeof unstable_observedBits === 'number' && Array.isArray(arguments[2])? '\n\nDid you call array.map(useContext)? ' +'Calling Hooks inside a loop is not supported. ' +'Learn more at https://reactjs.org/link/rules-of-hooks': '',);}// TODO: add a more generic warning for invalid values.if ((Context: any)._context !== undefined) {const realContext = (Context: any)._context;// Don't deduplicate because this legitimately causes bugs// and nobody should be using this in existing code.if (realContext.Consumer === Context) {console.error('Calling useContext(Context.Consumer) is not supported, may cause bugs, and will be ' +'removed in a future major release. Did you mean to call useContext(Context) instead?',);} else if (realContext.Provider === Context) {console.error('Calling useContext(Context.Provider) is not supported. ' +'Did you mean to call useContext(Context) instead?',);}}}return dispatcher.useContext(Context, unstable_observedBits);
}export function useState<S>(initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {const dispatcher = resolveDispatcher();return dispatcher.useState(initialState);
}export function useReducer<S, I, A>(reducer: (S, A) => S,initialArg: I,init?: I => S,
): [S, Dispatch<A>] {const dispatcher = resolveDispatcher();return dispatcher.useReducer(reducer, initialArg, init);
}export function useRef<T>(initialValue: T): {|current: T|} {const dispatcher = resolveDispatcher();return dispatcher.useRef(initialValue);
}export function useEffect(create: () => (() => void) | void,deps: Array<mixed> | void | null,
): void {const dispatcher = resolveDispatcher();return dispatcher.useEffect(create, deps);
}export function useLayoutEffect(create: () => (() => void) | void,deps: Array<mixed> | void | null,
): void {const dispatcher = resolveDispatcher();return dispatcher.useLayoutEffect(create, deps);
}export function useCallback<T>(callback: T,deps: Array<mixed> | void | null,
): T {const dispatcher = resolveDispatcher();return dispatcher.useCallback(callback, deps);
}export function useMemo<T>(create: () => T,deps: Array<mixed> | void | null,
): T {const dispatcher = resolveDispatcher();return dispatcher.useMemo(create, deps);
}export function useImperativeHandle<T>(ref: {|current: T | null|} | ((inst: T | null) => mixed) | null | void,create: () => T,deps: Array<mixed> | void | null,
): void {const dispatcher = resolveDispatcher();return dispatcher.useImperativeHandle(ref, create, deps);
}export function useDebugValue<T>(value: T,formatterFn: ?(value: T) => mixed,
): void {if (__DEV__) {const dispatcher = resolveDispatcher();return dispatcher.useDebugValue(value, formatterFn);}
}export const emptyObject = {};export function useTransition(): [(() => void) => void, boolean] {const dispatcher = resolveDispatcher();return dispatcher.useTransition();
}export function useDeferredValue<T>(value: T): T {const dispatcher = resolveDispatcher();return dispatcher.useDeferredValue(value);
}export function useOpaqueIdentifier(): OpaqueIDType | void {const dispatcher = resolveDispatcher();return dispatcher.useOpaqueIdentifier();
}export function useMutableSource<Source, Snapshot>(source: MutableSource<Source>,getSnapshot: MutableSourceGetSnapshotFn<Source, Snapshot>,subscribe: MutableSourceSubscribeFn<Source, Snapshot>,
): Snapshot {const dispatcher = resolveDispatcher();return dispatcher.useMutableSource(source, getSnapshot, subscribe);
}
-
定位到 useState
export function useState<S>(initialState: (() => S) | S, ): [S, Dispatch<BasicStateAction<S>>] {const dispatcher = resolveDispatcher();return dispatcher.useState(initialState); }
- 这边调用了一个
dispatcher.useState(initialState);
- 这个dispatch等于
const dispatcher = resolveDispatcher();
- 这边调用了一个
-
对应的再去找到这个 resolveDispatcher ,它是个function
function resolveDispatcher() {const dispatcher = ReactCurrentOwner.currentDispatcher;invariant(dispatcher !== null,'Hooks can only be called inside the body of a function component.',);return dispatcher; }
- 可看到是通过
ReactCurrentOwner.currentDispatcher
去获取的 - 这个就要涉及到后续react-dom渲染的过程
- 在我们使用阶段,是没有拿到任何真正节点的实例的。
- 比如在我们创建了react element,我们传进去的是这个class component的类
- 而不是它的一个new的一个实例,拿不到对应的东西
- 只有在 react-dom 进行真正的渲染的过程当中,才会去为我们创建这个实例
- 它这边提供了这个方法,在实际调用是要在我们的 react-dom进行渲染的时候
- 它才会为我们这个
ReactCurrentOwner
去设置属性
- 可看到是通过
-
ReactCurrentOwner 它是一个全局的类,定位一下
/*** 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 'react-reconciler/src/ReactFiber'; import typeof {Dispatcher} from 'react-reconciler/src/ReactFiberDispatcher';/*** Keeps track of the current owner.** The current owner is the component who should own any components that are* currently being constructed.*/ const ReactCurrentOwner = {/*** @internal* @type {ReactComponent}*/current: (null: null | Fiber),currentDispatcher: (null: null | Dispatcher), };export default ReactCurrentOwner;
-
我们可以看到 ReactCurrentOwner ,它就是一个对象,里面有两个属性
-
一个是 current , 就是这个current就对应正在目前正在渲染的哪一个节点的一个实例
-
currentDispatcher 是实例对应的 dispatcher
-
它一开始初始化的时候,是两个 null
-
然后到后期每一个组件进行渲染的时候,它才会进行一个渲染
-
就跟我们在class component里面看到的,它的 setState 调用的是 this.updateSetState
-
在这里的 dispatch 也是从不同平台上面,它传入进来的一个东西,不是我们在react中定义的
-
所以在react中我们可以看到的源码就非常的简单,就是这么一些相关的东西
-
同理,useEffect和其他内容也基本是类似的,只不过它最终调用的方法会不一样
-
比如说useReducer,那么它调用的是 dispatcher.useReducer
-
它们最终都是调用 dispatcher 上面的方法
相关文章:
React16源码: Hooks源码实现
Hooks 1 )概述 Hooks 在 React16.7版本出现的新功能Hooks 改变了整体应用开发的模式,同时开发体验会和以前会变得不一样Hooks 让函数组件具有类组件的能力 在 function component 里面没有this无法保存 state通过 Hooks可以让 function component 代替…...

华为端口隔离高级用法经典案例
最终效果: pc4不能ping通pc5,pc5能ping通pc4 pc1不能和pc2、pc3通,但pc2和pc3能互通 vlan batch 2 interface Vlanif1 ip address 10.0.0.254 255.255.255.0 interface Vlanif2 ip address 192.168.2.1 255.255.255.0 interface MEth0/0/1 i…...
java项目启动jar包启动参数设置端口号
默认启动 java -jar myapp.jar 指定配置文件 java -jar myapp.jar --spring.profiles.activedev 指定端口号 java -jar myapp.jar --server.port8080 后台启动 nohup java -jar myapp.jar --server.port8080 >outlog.log 2>&1 &...

【数据结构和算法】寻找数组的中心下标
其他系列文章导航 Java基础合集数据结构与算法合集 设计模式合集 多线程合集 分布式合集 ES合集 文章目录 其他系列文章导航 文章目录 前言 一、题目描述 二、题解 2.1 前缀和的解题模板 2.1.1 最长递增子序列长度 2.1.2 寻找数组中第 k 大的元素 2.1.3 最长公共子序列…...

多粒度在研究中的应用
FontDiffuser: One-Shot Font Generation via Denoising Diffusion with Multi-Scale Content Aggregation and Style Contrastive Learning 存在的问题 现有的字体生成方法虽然取得了令人满意的性能,但在处理复杂字和风格变化较大的字符(尤其是中文字符)时&#x…...
Docker命令---查看容器日志
介绍 使用docker命令查看容器输出的日志 示例 docker logs 容器ID...

Spring Boot 基于Redisson实现注解式分布式锁
依赖版本 JDK 17 Spring Boot 3.2.0 Redisson 3.25.0 源码地址:Gitee 导入依赖 <properties><redisson.version>3.25.0</redisson.version> </properties><dependencies><dependency><groupId>org.projectlombok</…...
Javascript 正则表达式零宽断言
在介绍正则表达式零宽断言这个概念之前,先看一下以下这道有关 javascript 正则表达式的题目: 登录注册流程是前端最常见的业务流程之一,注册流程少不了密码强弱度校验,请实现对密码的校验,要求满足: 包含大…...
Chocolatey
Chocolatey Software | PHP (Hypertext Preprocessor) 8.3.1 msi安装包https://github.com/chocolatey/choco/releases/download/2.2.2/chocolatey-2.2.2.0.msi 设置/安装 巧克力味Chocolatey CLI (choco)设置/安装 要求 受支持的 Windows 版本Windows …...

雍禾植发成毛发行业标杆!雍禾医疗获“年度医疗大健康消费企业”
近期,以“新视野 新链接”为主题的2023 EDGE AWARDS全球创新评选榜单正式发布。该评选由钛媒体发起,聚焦大健康产业,由权威行业专家、王牌分析师、专业投资机构、用户代表共同评审,兼顾综合专业性、影响力、创新性三大维度评选而出…...
Linux内核--进程管理(十二)共享内存和信号量
目录 一、引言 二、基础知识 三、统一封装的接口 ------>3.1、kern_ipc_perm 四、共享内存的创建和映射 ------>4.1、创建共享内存 ------>4.2、共享内存的映射 五、信号量的创建和使用 ------>5.1、信号量的创建 ------>5.2、信号量的初始化 ------…...
java 构造方法
构造方法 1、什么是构造方法,有什么用? 构造方法是一个比较特殊的方法,通过构造方法可以完成对象的创建,以及实例变量的初始化。 换句话说:构造方法是用来创建对象,并且同时给对象的属性赋值。 注意&#x…...
CISSP 第2章: 人员安全和风险管理概念
第二章 人员安全和风险管理概念 2.1 促进人员安全策略 构建工作描述方面的重要因素包括: 职责分离: 把关键的、重要的和敏感工作任务分配给若干不同的管理员或高级执行者,防止共谋 工作职责:最小特权原则 岗位轮换:提供知识冗余,减少伪造、数据更改、偷…...
前端八股文(CSS篇)一
目录 1.px和em的区别 2.介绍下BFC及其应用 3.介绍下粘性布局(sticky) 4.清除浮动的方法 5.如何用css或js实现多行文本溢出省略效果,考虑兼容 6.如何触发重排和重绘? 7.重绘与重排的区别? 8.说说两种盒模型以及区…...
游戏加速器LSP/DLL导致WSL.EXE无法打开问题修复!
解决办法: https://github.com/microsoft/WSL/issues/4177#issuecomment-597736482 方法一:(管理员身份) netsh winsock reset 方法二: WSCSetApplicationCategory 函数设置LSP加载权限 bool NoLsp(const wchar_t* …...

宏电股份5G RedCap终端产品助力深圳极速先锋城市建设
12月26日,“全城全网,先锋物联”深圳移动5G-A RedCap助力深圳极速先锋城市创新发布会举行,宏电股份携一系列5G RedCap终端产品应邀参与创新发布会,来自全国5G生态圈的各界嘉宾、专家学者济济一堂,共探信息化数字化创新…...
linux top命令中 cpu 利用率/mem 使用率与load average平均负载计算方式
文章目录 1 简介2 CPU% 字段3 MEM% 字段4 load average 平均负载 1 简介 top 命令是 Linux 上一个常用的系统监控工具,它经常用来监控 Linux 的系统状态,是常用的性能分析工具,能够显示较全的系统资源信息,包括系统负载ÿ…...

win11出现安全中心空白和IT管理员已限制对某些区域的访问(不一样的解决方式),真实的个人经历,并且解决经过
1、个人的产生问题的经历 2023年12月22日,由于我买了一块电脑的固态硬盘1T,想要扩容,原来电脑自带512G(由于个人是一个程序员,导致512G实在太古鸡肋)装好以后,想要重装一下系统,来个大清理。结果不出意料&…...
关于安卓重启设备和重启应用进程
android 重启应用进程 //多种方式重启应用进程public class MainActivity {//重启当前Applicationprivate void restartApplication(){final Intent intent getPackageManager().getLaunchIntentForPackage(getPackageName());intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP…...

Linux内核--进程管理(十三)O(1)调度算法
目录 一、引言 二、O(1)调度算法原理 ------>2.1、prio_array 结构 ------>2.2、runqueue 结构 三、实时进程调度 四、普通进程调度 ------>4.1、运行时间片计算 五、O(1)调度算法实现 ------>5.1、时钟中断任务调度 ------>5.2、任务调度 一、引言 …...
云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?
大家好,欢迎来到《云原生核心技术》系列的第七篇! 在上一篇,我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在,我们就像一个拥有了一块崭新数字土地的农场主,是时…...

简易版抽奖活动的设计技术方案
1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...

盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来
一、破局:PCB行业的时代之问 在数字经济蓬勃发展的浪潮中,PCB(印制电路板)作为 “电子产品之母”,其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透,PCB行业面临着前所未有的挑战与机遇。产品迭代…...
k8s从入门到放弃之Ingress七层负载
k8s从入门到放弃之Ingress七层负载 在Kubernetes(简称K8s)中,Ingress是一个API对象,它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress,你可…...

2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面
代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口(适配服务端返回 Token) export const login async (code, avatar) > {const res await http…...
大学生职业发展与就业创业指导教学评价
这里是引用 作为软工2203/2204班的学生,我们非常感谢您在《大学生职业发展与就业创业指导》课程中的悉心教导。这门课程对我们即将面临实习和就业的工科学生来说至关重要,而您认真负责的教学态度,让课程的每一部分都充满了实用价值。 尤其让我…...
DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”
目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...

初探Service服务发现机制
1.Service简介 Service是将运行在一组Pod上的应用程序发布为网络服务的抽象方法。 主要功能:服务发现和负载均衡。 Service类型的包括ClusterIP类型、NodePort类型、LoadBalancer类型、ExternalName类型 2.Endpoints简介 Endpoints是一种Kubernetes资源…...

C/C++ 中附加包含目录、附加库目录与附加依赖项详解
在 C/C 编程的编译和链接过程中,附加包含目录、附加库目录和附加依赖项是三个至关重要的设置,它们相互配合,确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中,这些概念容易让人混淆,但深入理解它们的作用和联…...
django blank 与 null的区别
1.blank blank控制表单验证时是否允许字段为空 2.null null控制数据库层面是否为空 但是,要注意以下几点: Django的表单验证与null无关:null参数控制的是数据库层面字段是否可以为NULL,而blank参数控制的是Django表单验证时字…...