当前位置: 首页 > news >正文

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 回调函数
  • 如果需要事件绑定什么的,之前在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 &#xff09;概述 Hooks 在 React16.7版本出现的新功能Hooks 改变了整体应用开发的模式&#xff0c;同时开发体验会和以前会变得不一样Hooks 让函数组件具有类组件的能力 在 function component 里面没有this无法保存 state通过 Hooks可以让 function component 代替…...

华为端口隔离高级用法经典案例

最终效果&#xff1a; pc4不能ping通pc5&#xff0c;pc5能ping通pc4 pc1不能和pc2、pc3通&#xff0c;但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 存在的问题 现有的字体生成方法虽然取得了令人满意的性能&#xff0c;但在处理复杂字和风格变化较大的字符(尤其是中文字符)时&#x…...

Docker命令---查看容器日志

介绍 使用docker命令查看容器输出的日志 示例 docker logs 容器ID...

Spring Boot 基于Redisson实现注解式分布式锁

依赖版本 JDK 17 Spring Boot 3.2.0 Redisson 3.25.0 源码地址&#xff1a;Gitee 导入依赖 <properties><redisson.version>3.25.0</redisson.version> </properties><dependencies><dependency><groupId>org.projectlombok</…...

Javascript 正则表达式零宽断言

在介绍正则表达式零宽断言这个概念之前&#xff0c;先看一下以下这道有关 javascript 正则表达式的题目&#xff1a; 登录注册流程是前端最常见的业务流程之一&#xff0c;注册流程少不了密码强弱度校验&#xff0c;请实现对密码的校验&#xff0c;要求满足&#xff1a; 包含大…...

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 &#xff08;choco&#xff09;设置/安装 要求 受支持的 Windows 版本Windows …...

雍禾植发成毛发行业标杆!雍禾医疗获“年度医疗大健康消费企业”

近期&#xff0c;以“新视野 新链接”为主题的2023 EDGE AWARDS全球创新评选榜单正式发布。该评选由钛媒体发起&#xff0c;聚焦大健康产业&#xff0c;由权威行业专家、王牌分析师、专业投资机构、用户代表共同评审&#xff0c;兼顾综合专业性、影响力、创新性三大维度评选而出…...

Linux内核--进程管理(十二)共享内存和信号量

目录 一、引言 二、基础知识 三、统一封装的接口 ------>3.1、kern_ipc_perm 四、共享内存的创建和映射 ------>4.1、创建共享内存 ------>4.2、共享内存的映射 五、信号量的创建和使用 ------>5.1、信号量的创建 ------>5.2、信号量的初始化 ------…...

java 构造方法

构造方法 1、什么是构造方法&#xff0c;有什么用&#xff1f; 构造方法是一个比较特殊的方法&#xff0c;通过构造方法可以完成对象的创建&#xff0c;以及实例变量的初始化。 换句话说&#xff1a;构造方法是用来创建对象&#xff0c;并且同时给对象的属性赋值。 注意&#x…...

CISSP 第2章: 人员安全和风险管理概念

第二章 人员安全和风险管理概念 2.1 促进人员安全策略 构建工作描述方面的重要因素包括: 职责分离: 把关键的、重要的和敏感工作任务分配给若干不同的管理员或高级执行者&#xff0c;防止共谋 工作职责:最小特权原则 岗位轮换:提供知识冗余&#xff0c;减少伪造、数据更改、偷…...

前端八股文(CSS篇)一

目录 1.px和em的区别 2.介绍下BFC及其应用 3.介绍下粘性布局&#xff08;sticky&#xff09; 4.清除浮动的方法 5.如何用css或js实现多行文本溢出省略效果&#xff0c;考虑兼容 6.如何触发重排和重绘&#xff1f; 7.重绘与重排的区别&#xff1f; 8.说说两种盒模型以及区…...

游戏加速器LSP/DLL导致WSL.EXE无法打开问题修复!

解决办法&#xff1a; https://github.com/microsoft/WSL/issues/4177#issuecomment-597736482 方法一&#xff1a;&#xff08;管理员身份&#xff09; netsh winsock reset 方法二&#xff1a; WSCSetApplicationCategory 函数设置LSP加载权限 bool NoLsp(const wchar_t* …...

宏电股份5G RedCap终端产品助力深圳极速先锋城市建设

12月26日&#xff0c;“全城全网&#xff0c;先锋物联”深圳移动5G-A RedCap助力深圳极速先锋城市创新发布会举行&#xff0c;宏电股份携一系列5G RedCap终端产品应邀参与创新发布会&#xff0c;来自全国5G生态圈的各界嘉宾、专家学者济济一堂&#xff0c;共探信息化数字化创新…...

linux top命令中 cpu 利用率/mem 使用率与load average平均负载计算方式

文章目录 1 简介2 CPU% 字段3 MEM% 字段4 load average 平均负载 1 简介 top 命令是 Linux 上一个常用的系统监控工具&#xff0c;它经常用来监控 Linux 的系统状态&#xff0c;是常用的性能分析工具&#xff0c;能够显示较全的系统资源信息&#xff0c;包括系统负载&#xff…...

win11出现安全中心空白和IT管理员已限制对某些区域的访问(不一样的解决方式),真实的个人经历,并且解决经过

1、个人的产生问题的经历 2023年12月22日&#xff0c;由于我买了一块电脑的固态硬盘1T&#xff0c;想要扩容&#xff0c;原来电脑自带512G(由于个人是一个程序员&#xff0c;导致512G实在太古鸡肋)装好以后&#xff0c;想要重装一下系统&#xff0c;来个大清理。结果不出意料&…...

关于安卓重启设备和重启应用进程

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、任务调度 一、引言 …...

【QT】发生的运行时错误汇总

1 、QObject::startTimer: Timers cannot be started from another thread 错误原因&#xff1a;QObject是可重入的&#xff0c;它的大多数非GUI子类&#xff0c;例如QTimer, QTcpSocket, QUdpSocket and QProcess都是可重入的&#xff0c;使得这些类可以同时用于多线程。需要…...

机器学习常用算法模型总结

文章目录 1.基础篇&#xff1a;了解机器学习1.1 什么是机器学习1.2 机器学习的场景1.2.1 模式识别1.2.2 数据挖掘1.2.3 统计学习1.2.4 自然语言处理1.2.5 计算机视觉1.2.6 语音识别 1.3 机器学习与深度学习1.4 机器学习和人工智能1.5 机器学习的数学基础特征值和特征向量的定义…...

笔记中所得(已删减)

1.交流电的一个周期内电压/电流的平均值都为0 2.电动势:电池将单位正电荷由负极搬到正极所做的功 5.额定能量:电池的额定容量乘以标称电压,以Wh为单位 6.500mAh意义是可以以500mA的电流放电1小时 7.电池容量的单位是mAh 13.实际电流源不能串联 14. 15. 16. 17. 18. 19.电…...

在Django5中使用Websocket进行通信

Docker安装Redis docker run --restartalways -p 6379:6379 --name redis -d redis:7.0.12 --requirepass zhangdapeng520安装依赖 参考文档&#xff1a;https://channels.readthedocs.io/en/latest/installation.html pip install "channels[daphne]"展示聊天页…...

外汇天眼:CySEC与NAGA Markets Europe达成15万欧元的和解

塞浦路斯证券交易委员会&#xff08;CySEC&#xff09;已经与NAGA Markets Europe达成15万欧元的和解。有关监管决定的会议于2023年3月举行&#xff0c;然而直到今天才公布这个决定。 该和解符合2009年塞浦路斯证券交易委员会法第37(4)条的规定&#xff0c;该条赋予CySEC就任何…...

Docker仓库搭建与镜像推送拉取

1.Docker镜像仓库 搭建镜像仓库可以基于Docker官方提供的DockerRegistry来实现。 官网地址&#xff1a;https://hub.docker.com/_/registry 1.1.简化版镜像仓库 Docker官方的Docker Registry是一个基础版本的Docker镜像仓库&#xff0c;具备仓库管理的完整功能&#xff0c;…...

最适合初学者的PHP集成环境!

如果你是一个php初学者&#xff0c;千万不要为了php的运行环境去浪费时间&#xff0c;这里我给大家推荐一款php的集成环境&#xff1a;phpStudy。它具备了php运行的三要素&#xff1a;php、apache、mysql&#xff0c;当然它具备的功能远不止这些。 phpstudy V8安装步骤 步骤一…...

添加 Android App Links

添加 Android App Links功能 介绍一个简单的效果Android配置Add Url intent filtersAdd logic to handle the intentAssociate website 搭建网页支持AppLinks 介绍 Android App Links 是指将用户直接转到 Android 应用内特定内容的 HTTP 网址。Android App Links 可为您的应用带…...

五、Spring AOP面向切面编程(基于注解方式实现和细节)

本章概要 Spring AOP底层技术组成初步实现获取通知细节信息切点表达式语法重用&#xff08;提取&#xff09;切点表达式环绕通知切面优先级设置CGLib动态代理生效注解实现小结 5.5.1 Spring AOP 底层技术组成 动态代理&#xff08;InvocationHandler&#xff09;&#xff1a;…...

ES6 class详解

✨ 专栏介绍 在现代Web开发中&#xff0c;JavaScript已经成为了不可或缺的一部分。它不仅可以为网页增加交互性和动态性&#xff0c;还可以在后端开发中使用Node.js构建高效的服务器端应用程序。作为一种灵活且易学的脚本语言&#xff0c;JavaScript具有广泛的应用场景&#x…...

汉沽网站建设制作/市场营销培训

目录 一&#xff1a;介绍 二&#xff1a;计时器Timer类的编写 三&#xff1a;计时器管理TimerManager类的编写 四&#xff1a;使用方法 一&#xff1a;介绍 这里我们使用List来管理定义的所有计时器Timer&#xff0c;首先就是需要构建计时器的完整逻辑&#xff0c;包括计时…...

南宁营销网站建设/运营培训班有用吗

目前最主流的四个垃圾回收器分别是&#xff1a;Serial收集器&#xff08;常用于单CPU环境&#xff09;、Throughput&#xff08;或者Parallel&#xff09;收集器&#xff0c;Concurrent&#xff08;CMS&#xff09;收集器和G1收集器 垃圾回收逻辑 对于程序员而言&#xff0c;…...

html5网站建设基本流程/枸橼酸西地那非片多长时间见效

Content-Type: text/html如果您设置此标头意味着您必须向用户发送HTML.您可以决定使用像TinyMCE这样的东西让用户在Word样式的编辑器中编写消息并使用它的HTML输出.或者将标题设置为纯文本.Content-Type: text/plain编辑&#xff1a;试试这个$to exampleexample.com;$subject …...

wordpress 排除指定分类/站长之家ip查询工具

最近做项目&#xff0c;遇到了Ajax同步异步和跨域的问题&#xff0c;整理总结如下&#xff1a;关于同步、异步手册中时这样写的&#xff0c;默认是异步&#xff0c;为了并行加载&#xff0c;尽量不要设置为同步&#xff0c;因为同步时浏览器会被锁死。但根据实际情况而定&#…...

网站是如何盈利/电商网站建设报价

...

酷 网站模板/seo服务 收费

...