React原理 - React Hooks

news2024/11/26 3:50:21

目录

扩展学习资料

React Hooks 编写函数组件

Hooks使命

Hooks解决了什么问题

Hooks原理

useState源码解析

mountState源码解析

Hooks应用

Hooks 实践

倒计时组件

练习


扩展学习资料

名称

链接

React Hooks 官方文档

Introducing Hooks – React

useEffect 完整指南

useEffect 完整指南 — Overreacted

React Hooks 编写函数组件

Hooks使命

  • 逻辑与UI分离 :React官方推荐在开发中将逻辑部分与视图部分解耦,便于定位问题和职责清晰。
  • 函数组件拥有state:在函数组件中如果要实现类似拥有state的状态,必须要将组件转成class组件。
  • 逻辑组件复用:社区一直致力于逻辑层面的复用,像render props / HOC【高阶组件】,不过他们都有对应的问题。Hooks是目前为止相对完美的解决方案。

Hooks解决了什么问题

render props

  • 通过渲染props来实现逻辑组件复用。
  • Avatar组件时一个渲染头像的组件,里面包含其中一些业务逻辑,User组件时纯UI组件展示用户名称。
  • render props 通过嵌套组件实现,在真实的业务中,会出现嵌套多层,以及梳理props不清晰的问题。
export default function App() {
    return (
        <div className="APP">
        	<Avatar name="云">
         		{name => <User name={name} />}
          </Avatar>
        </div>    
    );
}

HOC【高阶组件】

  • 通过对现有组件进行扩展、增强的方式来实现复用,通常采用包裹方法来实现。
  • 高阶组件的实现会额外地增加元素层级,使得页面元素的数量更加臃肿
class Avatar extends Component{
    render() {
        return <div> {this.props.name} </div>;    
    }
}
function HocAvatar(Component) {
    return ()=><Component name="云" />;
}
export default HocAvatar(Avatar);

Hooks

  • React16.8引入的Hooks,使得实现相同功能而代码量更少成为现实。
  • 通过使用Hooks,不仅在硬编码层面减少代码的数量,同样的在编译之后的代码也会更少。
import React, {useState} from "react";
export function HooksAvatar() {
    const [name, setName] = useState("云");
    return <>{name}</>;
}

Hooks原理

并不是复杂的diff逻辑,仅仅是一个数组arrays

Hooks的demo

  • 下面是一个非常简单的Hook API,创建了name和setName。在页面上展示name,按钮的点击事件修改name。
  • 那么在这个过程中setState是如何实现的呢?
import React, {useState} from 'react';
import {render} from 'react-dom';

function App() {
    const [name, setName] = useState('云');
    
    return (
        <div>
        	<div>{name}</div>
         <button type="button" onClick={() => setName('hooks 原理')}>修改</button>
        </div>    
    );
}

useState源码解析

  • useState API虽然是在react中引入的,其内部实现是在react-reconciler包中完成的。
  • 在try/catch代码部分,调用了mountState方法。
  • 顺着这个方法,我们去探寻一些mountState的实现。
useState<S>(
    initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
    currentHookNameInDec = 'useState';
    mountHookTypesDev();
    const prevDispatcher = ReactCurrentDispatcher.current;
    ReactCurrentDispatcherOnMountInDEV;
    try {
        // 调用mountState(初始化值)
        return mountState(initialState); 
    } finally {
        ReactCurrentDispatcher.current = prevDispatcher;    
    }
}

mountState源码解析

mountState方法内:

  1. 先返回当前执行中的Hook对象,Hook对象内包含memoizedState/baseState/queue等属性。
  2. 接着会将下次要渲染的state值和修改state的方法返回。
  3. 最后将这两个值以数组的形式返回。

如果方法里面有多个useState方法,如何让这些按期望的顺序执行呢?

function mountState<S>(
    initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
    // 返回当前正在运行的hook对象
    const hook = mountWorkInProgressHook();
    if(typeof initialState === 'function') {
        // 是函数,就执行函数,拿return 值
        // $FlowFixMe: Flow doesn't like mixed types
        initialState = initialState();    
    }
    // 字符串,就赋值
    hook.memoizedState = hook.baseState = initialState;
    // 定义一个队列,相当于queue = {...}
    const queue = (hook.queue = {
        pending: null,
        dispatch: null,
        lastRenderedReducer: basicStateReducer,
        lastRebderedState: (initialState: any),    
    });
    // 这个dispatch就是我们在外面拿到的哪个修改值的方法
    const dispatch: Dispatch<BasicStateAction<S>,>
    = (queue.dispatch = (dispatchAction,bind(null, currentlyRenderingFiber, queue, ) : any));
    // [name, setName] = useState(initialState)
    return [hook.memoizedState, dispatch];
}

  • 在初始化时,每一次申明useState就左图所示,会生成一对state/setter映射。接着每次渲染都会按照这个顺序从数组最小下标遍历到最大值。
  • 在前面代码(mountState)中,我们说会先返回一个hook对象。state值(memoizedState)和返回的setXXX都会关联到这个hook对象。因此在触发某一个setXXX方法的时候可以正确地设置memoizedState值。
  • 所以初始化执行useState时,不能写在条件判断里,会打乱顺序。要在后面业务中判断。

 

Hooks应用

Hooks 实践

Hooks官方API

  • useState: 函数组件中的state方法
  • useEffect(fn, time):函数组件处理副作用的方法【异步接口,setTimeout...】useEffect(fn, null) ~=componentDidMount
  • useReducer:另一种‘useState’,跟redux有点类似
  • useRef:返回一个突变的ref对象,对象在组件的生命周期内一直存在。【执行一些一直都在的方法】
  • useCustom:自定义Hooks组件

倒计时组件

CountDown组件-1


倒计时组件在我们日常的开发中十分常见。我们就以这个组件的开发来实践Hooks。

  1. ·首先声明基本变量,这里申明了三个变量,分别表示按钮默认的文案、倒计时的数字、是否开启倒计时。
  2. 绑定button点击事件。
const maxTine = 10;
const [countDownText, setCountDownText] = useState('获取验证码');
const [timerNum,setTimerNum] = useState(maxTime);
const [boolCountDown,setBoolCountDown] = useState(false);
const handleClick = () => {
    // 开始倒计时
}
return (
    <div>
    	<button type="button" onClick={handleClick}>
     		{boolCountDown ? `${timerNum}s`} : countDownText}
     	</button>
    </div>
)

CountDown组件-2


  1. 处理点击事件,判断当前是否在倒计时中,否则开启倒计时。
  2. useEffect依赖boolCountDown变量,然后开启计时。这里因为倒计时是突变的,所以把这部分代码放到refCountDown.current属性中。
  3. 将具体的倒计时判断逻辑方法赋值给current属性。至此倒计时组件完成。
const refCountDown = useRef(null);// 挂载倒计时的载体
const handleClick = () => {
    if(boolCountDown) {
        setBoolCountDown(true);  
    }
};

refCountDown.current = () => { // 具体逻辑处理放在current属性上
    if (timerNum > 0) {
        setTimerNum(timerNum - 1);
    } else {// 倒计时结束,重置状态
        setTimerNum(maxTime);
        setBoolCountDown(false);
        setCountDownText('重新获取');    
    }
}
useEffect(()=>{
    if(boolCountDown) { // true 开始倒计时
        const timer = setInterval(()=>{
            refCountDown.current();        
        }, 1000);
        return () => clearInterval(timer);    
    }
}, [boolCountDown]); // 检测到boolCountDown变化,就会执行

 Hooks完整版倒计时组件

import React, { useState, useEffect, useRef } from "react";
export default function CountDown() {
  const maxTime = 60;
  const [countDownText, setCountDownText] = useState("获取验证码");
  const [timerNum, setTimerNum] = useState(maxTime);
  const [boolCountDown, setBoolCountDown] = useState(false);
  const refCountDown = useRef(null);
  const handleClick = () => { // 按钮事件是否开启
    if (!boolCountDown) {
      setBoolCountDown(true);
    }
  };
  refCountDown.current = () => { // 如果timerNum不绑定在这里,它不会实时变化
    if (timerNum > 0) {
      setTimerNum(timerNum - 1);
    } else {
      setTimerNum(maxTime);
      setBoolCountDown(false);
      setCountDownText("重新获取");
    }
  };
  // const otherCountDown = () => {
  //   console.log(timerNum, "timerNum");
  //   setTimerNum((timerNum) => timerNum - 1);
  // };
  useEffect(() => {
    if (boolCountDown) {
      const timer = setInterval(() => {
        // otherCountDown();
        refCountDown.current();
      }, 1000);
      return () => clearInterval(timer);// 执行一次,就清空一下定时
    }
  }, [boolCountDown]); // 根据这个参数监控的,想当componentDidMount使用就置空参数,就会只执行一次
  return (
    <div>
      <button type="button" onClick={handleClick}>
        {boolCountDown ? `${timerNum}s` : countDownText}
      </button>
    </div>
  );
}

练习

1.用Hooks 实现一个倒计时组件

2.用class完成一个倒计时组件

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

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

相关文章

PYTHON知识点学习-列表和元组

&#x1f308;write in front&#x1f308; &#x1f9f8;大家好&#xff0c;我是Aileen&#x1f9f8;.希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流. &#x1f194;本文由 Aileen_0v0&#x1f9f8; 原创 CSDN首发&#x1f412; 如…

把一般数据转换成因子数据格式,做单因子、债券对历史数据回测+获取curl命令+垃圾数据转换成标准行情数据(bardata)

下载curl软件&#xff0c;地址&#xff1a; curl for Windows for 64-bit下载好后解压到文件夹&#xff0c;将里面的bin文件添加到环境变量中&#xff0c;bon文件地址为&#xff1a;C:\Users\59980\curl-8.2.1_7-win64-mingw\bin 打开cmd&#xff0c;输入curl --help,出现下…

软考:中级软件设计师:程序语言基础:表达式,标准分类,法律法规,程序语言特点,函数传值传址

软考&#xff1a;中级软件设计师:程序语言基础&#xff1a;表达式 提示&#xff1a;系列被面试官问的问题&#xff0c;我自己当时不会&#xff0c;所以下来自己复盘一下&#xff0c;认真学习和总结&#xff0c;以应对未来更多的可能性 关于互联网大厂的笔试面试&#xff0c;都…

ssm民宿管理系统源码和论文

ssm民宿管理系统源码和论文110 开发工具&#xff1a;idea 数据库mysql5.7 数据库链接工具&#xff1a;navcat,小海豚等 技术&#xff1a;ssm 摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&…

SSM整合~

构建并配置项目&#xff1a; 第一步&#xff1a;创建maven项目 第二步&#xff1a;配置pom.xml文件 设置打包方式&#xff1a; <packaging>war</packaging>设置版本号为自定义属性&#xff1a; <properties><!--将版本号通过自定义属性配置--><…

跨站请求伪造(CSRF)攻击与防御原理

跨站请求伪造&#xff08;CSRF&#xff09; 1.1 CSRF原理 1.1.1 基本概念 跨站请求伪造&#xff08;Cross Site Request Forgery&#xff0c;CSRF&#xff09;是一种攻击&#xff0c;它强制浏览器客户端用户在当前对其进行身份验证后的Web 应用程序上执行非本意操作的攻击&a…

差异化竞争阵地的所在【周技术进阶】-从BS 项目C#最基础截取字符串方法开始

效果 代码 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;namespace ConsoleAppNumberOneHelloWorld {class Program{static void Main(string[] args){Console.WriteLine("hello world&#xf…

TCP机制之确认应答及超时重传

TCP因为其可靠传输的特性被广泛使用,这篇博客将详细介绍一下TCP协议是如何保证它的可靠性的呢?这得主要依赖于其确认应答及超时重传机制,同时三次握手四次挥手也起到了少部分不作用,但是主要还是由确认应答和超时重传来决定的;注意:这里的可靠传输并不是说100%能把数据发送给接…

JVM学习(五)--方法区

概念&#xff1a; 方法区就是存和类相关的东西&#xff0c;成员方法&#xff0c;方法参数&#xff0c;成员变量&#xff0c;构造方法&#xff0c;类加载器等&#xff0c;逻辑上存在于堆中&#xff0c;但是不同的虚拟机对它的实现不同&#xff0c;oracle的hotsport vm在1.6的时…

事务(SQL)

事务概述 事务是一组操作的集合&#xff0c;他是一个不可分割的工作单位&#xff0c;事务会把所有的操作作为一个整体一起向西永提交或撤销操作请求。这组操作&#xff0c;要么全部执行成功&#xff0c;要么全部执行失败。 事务操作 查看/设置事务提交方式 -- 查看/设置事务…

9.1.tensorRT高级(4)封装系列-自动驾驶案例项目self-driving-道路分割分析

目录 前言1. 道路分割总结 前言 杜老师推出的 tensorRT从零起步高性能部署 课程&#xff0c;之前有看过一遍&#xff0c;但是没有做笔记&#xff0c;很多东西也忘了。这次重新撸一遍&#xff0c;顺便记记笔记。 本次课程学习 tensorRT 高级-自动驾驶案例项目self-driving-道路分…

Linux入门之多线程|线程|进程基本概念及库函数

目录 一、线程 1.线程的概 补充知识点&#xff1a;页表 2.线程的优点 3.线程的缺点 4.线程异常 5.线程用途 二、线程与进程的区别与联系 三、关于进程线程的问题 0.posix线程库 1.创建线程 2.线程终止 3.取消线程 4.线程等待&#xff08;等待线程结束&#xff09;…

02|李沐动手学深度学习v2(笔记)

基础优化算法 导航 基础优化算法梯度下降1.1 小批量随机梯度下降1.2 小结 线性回归实现1. 处理数据1.3 生成大小为batch_size的小批量 2. 处理模型3. 模型评估4. 训练过程 梯度下降 针对我们的模型没有显示解。&#xff08;生活中很少能有完全符合的线性模型&#xff0c;大多数…

用户中心笔记-leovany

1. 安装 官方地址&#xff1a;https://pro.ant.design/zh-CN/docs/getting-started 1.1 Mac系统 1.1.1 安装yarn 安装yarn brew install yarn查看版本 brew -v 1.1.2 安装node // 安装node brew install node // 关联 brew unlink node && brew link node // 查看版…

信息系统安全运维模型 课堂记录

声明 本文是学习 信息系统安全运维管理指南. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 范围 本标准描述了信息系统安全运维管理体系&#xff0c;给出了安全运维策略、安全运维组织、安全运维规程和安全运维支撑系统等方面相关活动的目的、要求和…

【项目 计网9】4.25 IO多路复用简介 4.26select API介绍 4.27 select代码编写

文章目录 4.25 IO多路复用&#xff08;I/O多路转接&#xff09;简介4.26select API介绍4.27 select代码编写客户端程序select程序select的缺点 4.25 IO多路复用&#xff08;I/O多路转接&#xff09;简介 输入输出&#xff1a;以内存为主体 读写&#xff1a;以程序为主体 程序要…

2023-09-03 LeetCode每日一题(消灭怪物的最大数量)

2023-09-03每日一题 一、题目编号 1921. 消灭怪物的最大数量二、题目链接 点击跳转到题目位置 三、题目描述 你正在玩一款电子游戏&#xff0c;在游戏中你需要保护城市免受怪物侵袭。给你一个 下标从 0 开始 且长度为 n 的整数数组 dist &#xff0c;其中 dist[i] 是第 i …

从一到无穷大 #12 Planet-Scale In-Memory Time Series Database, Is it really Monarch?

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。 本作品 (李兆龙 博文, 由 李兆龙 创作)&#xff0c;由 李兆龙 确认&#xff0c;转载请注明版权。 文章目录 引言约束优势数据模型写路径查询路径Field Hints Index可靠性 其他总结 引言 Monarc…

Thymeleaf常见属性

参考文档 thymeleaf 语法——th:text默认值、字符串连接、th:attr、th:href 传参、th:include传参、th:inline 内联、th:each循环、th:with、th:if_猎人在吃肉的博客-CSDN博客 代码演示 Controller public class TestController {AutowiredMenuService menuService;GetMapp…

基于多设计模式下的同步异步日志系统

基于多设计模式下的同步&异步日志系统 代码链接&#xff1a;https://github.com/Janonez/Log_System 1. 项目介绍 本项目主要实现一个日志系统&#xff0c; 其主要支持以下功能&#xff1a; 支持多级别日志消息支持同步日志和异步日志支持可靠写入日志到标准输出、文件…