【React二】ref与事件处理与生命周期钩子函数

news2025/7/8 21:42:20

关于react基础及组件化编程的可以参考本篇笔记

文章目录

  • 一、 React中的事件处理
    • 1-1 例子: 不要过度使用ref
    • 1-2 受控组件与非受控组件
      • 1-2-1 受控组件
      • 1-2-2 非受控组件
    • 1-3 高阶函数
      • 1-3-1 函数的柯里化
      • 1-3-2 不用柯里化的写法
  • 二、生命周期
    • 2-1 引出生命周期
    • 2-2 组件生命周期
      • 2-2-1 生命周期钩子【新旧】
      • 2-2-2 生命周期的三个阶段(旧)
        • 2-2-2-1 基本介绍
        • 2-2-2-2 例子 setState触发更新
        • 2-2-2-3 例子 componentWillReceiveProps :
      • 2-2-3 生命周期的三个阶段(新)
      • 2-2-4 DOM的diffing算法

一、 React中的事件处理

建议使用非受控组件

  • 不要过度使用ref

1-1 例子: 不要过度使用ref

  1. 通过onXxx属性指定事件处理函数(注意大小写)
    • a.React使用的是自定义(合成)事件,而不是使用的原生DOM事件 —为了更好的兼容性
    • b.React中的事件是通过事件委托方式处理的 (委托给组件最外层的元素)—为了高校
  2. 通过event.target得到发生事件的DOM元素对象----不要过度使用ref
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="test"></div>
    <div id="test2"></div>
    <div id="test3"></div>
    <script type="text/javascript" src="../js/react.development.js"></script>
    <script
      type="text/javascript"
      src="../js/react-dom.development.js"
    ></script>
    <script type="text/javascript" src="../js/babel.min.js"></script>

    <script type="text/babel">
      // 创建组件
      class Demo extends React.Component {
        showData2 = (event) => {
          // alert(this.myRef2.current.value);
          alert(event.target.value);
        };
        render() {
          return (
            <div>
              <input type="text" onBlur={this.showData2} />
            </div>
          );
        }
      }

      ReactDOM.render(<Demo />, document.getElementById("test"));
    </script>
  </body>
</html>

1-2 受控组件与非受控组件

1-2-1 受控组件

  1. 类似vue中的双向数据绑定
    • 所有输入类的DOM,将输入维护在state中,使用时直接从状态中取出来

image-20221102170742270

  <script type="text/babel">
      class Login extends React.Component {
        // 初始化状态
        state = {
          username: "", // 用户名
          password: "", // 密码
        };

        saveUsername = (event) => {
          this.setState({ username: event.target.value });
        };

        savePassword = (event) => {
          this.setState({ password: event.target.value });
        };
        handleSubmit = (event) => {
          event.preventDefault(); // 组织表单提交
          const { username, password } = this;
          alert(
            `你输入的用户名是: ${username.value},你输入的密码是:${password.value}`
          );
        };
        render() {
          return (
            <form action="http" onSubmit={this.handleSubmit}>
              用户名:
              <input type="text" onChange={this.saveUsername} name="username" />
              密码:
              <input
                type="password"
                name="password"
                onChange={this.savePassword}
              />
              <button>登录</button>
            </form>
          );
        }
      }

      // 渲染组件
      ReactDOM.render(<Login />, document.getElementById("test"));
    </script>

1-2-2 非受控组件

现用现取

   <script type="text/babel">
      class Login extends React.Component {
        handleSubmit = (event) => {
          event.preventDefault(); // 阻止表单提交
          const { username, password } = this;
          alert(`你输入的用户名是${username.value},密码是${password.value}`);
        };
        render() {
          return (
            <form onSubmit={this.handleSubmit}>
              用户名:
              <input
                ref={(c) => (this.username = c)}
                type="text"
                name="username"
              />
              密码:
              <input
                ref={(c) => (this.password = c)}
                type="text"
                name="password"
              />
              <button>登录</button>
            </form>
          );
        }
      }

      // 渲染组件
      ReactDOM.render(<Login />, document.getElementById("test"));
    </script>

1-3 高阶函数

  1. 如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数
    • 若A函数,接收的参数是一个函数,那么A就可以成为高阶函数
    • 若A函数,调用的返回值依然是一个函数,那么A就可以称为高阶函数
  2. 常见的高阶函数: Promise/setTimeout/arr.map()等等
   saveFormData = (dataType) => {
          // 无关的输出
          console.log(dataType);
          // 真正返回的值是函数
          return (event) => {
            console.log(dataType, event.target.value);
            // this.setState({ password: event.target.value });
          };
        };

1-3-1 函数的柯里化

  1. 函数的柯里化: 通过函数调用继续返回函数的方式。实现多次接收参数最后统一处理的函数编码形式
function(a) {
	function(b) {
		function(c) {
			return a+b+c;
		}
	}
}

1-3-2 不用柯里化的写法

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="test"></div>
    <div id="test2"></div>
    <div id="test3"></div>
    <script type="text/javascript" src="../../js/react.development.js"></script>
    <script
      type="text/javascript"
      src="../../js/react-dom.development.js"
    ></script>

    <script type="text/javascript" src="../../js/babel.min.js"></script>

    <script type="text/babel">
      class Login extends React.Component {
        // 初始化状态
        state = {
          username: "", // 用户名
          password: "", // 密码
        };

        // 保存表单数据到状态中
        saveFormData = (dataType, event) => {
          this.setState({ [dataType]: event.target.value });
        };

        handleSubmit = (event) => {
          event.preventDefault(); // 组织表单提交
          const { username, password } = this.state;
          alert(`你输入的用户名是: ${username},你输入的密码是:${password}`);
        };
        render() {
          return (
            <form action="http" onSubmit={this.handleSubmit}>
              用户名:
              <input
                type="text"
                // 些事件回调的时候不要加小括号,加了就是将saveFormData的返回值作为onChange的回调;返回值是undefined
                onChange={(event) => this.saveFormData("username", event)}
                name="username"
              />
              密码:
              <input
                type="password"
                name="password"
                onChange={((event) => this.saveFormData("password"), event)}
              />
              <button>登录</button>
            </form>
          );
        }
      }

      // 渲染组件
      ReactDOM.render(<Login />, document.getElementById("test"));
    </script>
  </body>
</html>

二、生命周期

2-1 引出生命周期

在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="test"></div>
    <div id="test2"></div>
    <div id="test3"></div>
    <script type="text/javascript" src="./js/react.development.js"></script>
    <script type="text/javascript" src="./js/react-dom.development.js"></script>

    <script type="text/javascript" src="./js/babel.min.js"></script>

    <script type="text/babel">
      class Life extends React.Component {
        state = { opacity: 0.5 };
        death = () => {
          // 卸载组件
          ReactDOM.unmountComponentAtNode(document.getElementById("test"));
          // 消除定时器
          clearInterval(this.timer);
        };
        // action = () => {
        //   setInterval(() => {
        //     let { opacity } = this.state;
        //     // 减小0.1
        //     opacity -= 0.1;
        //     if (opacity <= 0) opacity = 1;
        //     // 设置新的透明度
        //     this.setState({ opacity });
        //   }, 200);
        // };

        // 初始化渲染、状态更新之后
        // 该状态就重新改变render
        render() {
          console.log("render");
          // setInterval(() => {
          //   let { opacity } = this.state;
          //   opactiy -= 0.1;
          //   if (opacity <= 0) {
          //     opacity = 1;
          //   }
          //   this.setState({ opacity });
          // }, 200);
          return (
            <div>
              <h2 style={{ opacity: this.state.opacity }}>
                React学不会怎么办?
              </h2>
              <button onClick={this.death}>不活了</button>
            </div>
          );
        }

        // 组件挂载完毕
        componentDidMount() {
          console.log("@");
          this.timer = setInterval(() => {
            let { opacity } = this.state;
            opacity -= 0.1;
            if (opacity <= 0) {
              opacity = 1;
            }
            this.setState({ opacity });
          }, 200);
        }

        // 组件将要卸载
        componentWillUnmount() {
          // 消除定时器
          clearInterval(this.timer);
        }
      }
      // 渲染组件
      ReactDOM.render(<Life />, document.getElementById("test"));
    </script>
  </body>
</html>

2-2 组件生命周期

2-2-1 生命周期钩子【新旧】

react生命周期(旧)

react生命周期(旧)

react生命周期新

react生命周期(新)

2-2-2 生命周期的三个阶段(旧)

2-2-2-1 基本介绍

  1. 初始化阶段: 由ReactDOM.render()触发----初次渲染

    • constructor()

    • componentWillMount() : 组件将要挂载的钩子

    • render()

    • componentDidMount()

      • 一般在这个钩子中做一些初始化的事,例如: 开启定时器、发送网络请求、订阅消息
      • 组件挂载完毕的钩子
  2. componentWillReceiveProps :

    • 组件将要接收props调用
  3. 更新阶段: 由组件内部this.setState()或父组件重新render触发

    • shouldComponentUpdate()
      • 阀门:组件是否应该被更新
      • 不写持续返回true
    • componentWillUpdate()
    • render()
    • componentDidUpdate()
  4. 卸载组件:由ReactDOM.unmountComponentAtNode()触发

    • componentWillUnmount()
    • 关闭定时器、取消订阅消息

2-2-2-2 例子 setState触发更新

强制更新不受阀门控制

  1. 代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="test"></div>
    <div id="test2"></div>
    <div id="test3"></div>
    <script type="text/javascript" src="./js/react.development.js"></script>
    <script type="text/javascript" src="./js/react-dom.development.js"></script>

    <script type="text/javascript" src="./js/babel.min.js"></script>

    <script type="text/babel">
      // 创建组件
      class Count extends React.Component {
        // 构造器
        constructor(props) {
          console.log("Count-constructor");
          super(props);
        }
        // 初始化状态
        state = { count: 0 };
        // 加1按钮的回调
        add = () => {
          // 获取原状态
          const { count } = this.state;
          // 更新状态
          this.setState({ count: count + 1 });
        };

        // 卸载组件按钮的回调
        death = () => {
          ReactDOM.unmountComponentAtNode(document.getElementById("test"));
        };
        //强制更新按钮的回调
        force = () => {
          this.forceUpdate();
        };

        // 组件将要挂载的钩子
        componentWillMount() {
          console.log("Count---componentWillMount");
        }

        // 组件挂载完毕的钩子
        componentDidMount() {
          console.log("Count---componentDidMount");
        }

        // 组件将要卸载的钩子
        componentWillUnmount() {
          console.log("Count--componentWillUnmount");
        }

        // 控制组件更新的阀门
        shouldComponentUpdate() {
          console.log("Count---shouldComponentUpdate");
          return true;
        }

        // 组件将要更新的钩子
        componentWillUpdate() {
          console.log("Count--componentWillUpdate");
        }
        componentDidUpdate() {
          console.log("Count---componentDidUpdate");
        }

        render() {
          console.log("Count---render");
          const { count } = this.state;
          return (
            <div>
              <h2>当前求和为: {count}</h2>
              <button onClick={this.add}>点我—+1</button>
              <button onClick={this.death}>卸载组件</button>
            </div>
          );
        }
      }

      // 渲染组件
      ReactDOM.render(<Count />, document.getElementById("test"));
    </script>
  </body>
</html>

  1. 展示

在这里插入图片描述

在这里插入图片描述

2-2-2-3 例子 componentWillReceiveProps :

  • 组件将要接收props调用
  • 第一次传递的props不算
  • 还可以接收参数

在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="test"></div>
    <div id="test2"></div>
    <div id="test3"></div>
    <script type="text/javascript" src="./js/react.development.js"></script>
    <script type="text/javascript" src="./js/react-dom.development.js"></script>

    <script type="text/javascript" src="./js/babel.min.js"></script>

    <script type="text/babel">
      // 父组件render流程
      class A extends React.Component {
        // 初始化状态
        state = { carName: "奔驰" };
        changeCar = () => {
          this.setState({ carName: "奥托" });
        };
        render() {
          return (
            <div>
              <div>我是A组件</div>
              <button onClick={this.changeCar}>换车</button>
              <B carName={this.state.carName} />
            </div>
          );
        }
      }
      class B extends React.Component {
        componentWillReceiveProps() {
          console.log("B---componentWillReceiveProps");
        }
        render() {
          return <div>我是B组件,接收到的车是: {this.props.carName}</div>;
        }
      }
      // 渲染组件
      ReactDOM.render(<A />, document.getElementById("test"));
    </script>
  </body>
</html>

2-2-3 生命周期的三个阶段(新)

getDerivedStateFormProps(得到派生的状态)

  • UNSAFE_componentWillMount():组件将要挂载的钩子
  • UNSAFE_componentWillUpdate: 组件将要更新的钩子
  1. 初始化阶段: 由ReactDOM.render()触发----初次渲染

    • constructor()

    • getDerivedStateFormProps

    • render()

    • componentDidMount()

      • 一般在这个钩子中做一些初始化的事,例如: 开启定时器、发送网络请求、订阅消息
      • 组件挂载完毕的钩子
  2. 更新阶段: 由组件内部this.setState()或父组件重新render触发

    • getDerivedStateFormProps
    • shouldComponentUpdate()
    • render()
    • getSnapshotUpdate()
    • componentDidUpdate()
  3. 卸载组件:由ReactDOM.unmountComponentAtNode()触发

    • componentWillUnmount()

2-2-4 DOM的diffing算法

  1. 经典面试题:
    • react/vue中的key有什么作用?(key的内部原理是什么?)
    • 为什么遍历列表,key最好不要用index
  2. 虚拟DOM中key的作用:
    • key是虚拟DOM对象的标识,在更新显示时key起着极其重要的作用
    • 当状态中的数据发生改变时,react会根据【新数据】生成【新的虚拟DOM】,随后React进行【新虚拟DOM】和【旧虚拟DOM】的diff比较
      • 旧虚拟DOM中找到了与新虚拟DOM相同的key :
        • 若虚拟DOM中内容没变,直接使用之前真实DOM
        • 若虚拟DOM中内容变了,则生成新的真实DOM
      • 旧虚拟DOM中未找到与新虚拟DOMi相同的key
        • 根据数据创建新的真实DOM,随后渲染到页面
  3. 用index作为key可能会引发的问题:
    • 弱队数据进行:逆序添加、逆序删除等破坏顺序操作
      • 会产生没有必要的真实DOM更新 ===> 界面效果没问题,但效率低
    • 如果结果还包含输入类DOM :
      • 会产生错误DOM更新–> 界面有问题
    • 如果不存在对数据的逆序添加、逆序删除等破环删除操作
      • 仅用于渲染列表用于展示,使用index作为key没有问题

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

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

相关文章

TiDB 数据库架构概述

TiDB 数据库架构概述 TiDB体系架构 水平扩容或者缩容金融级高可用实时HTAP云原生的分布式数据兼容MYSQL5.7 Region&#xff1a;存储单位&#xff0c;96~144MB之间,分布式存储在TiKV中 TiKV&#xff1a;默认三副本&#xff0c;将分布式数据存储&#xff0c;并进行存储副本。…

【ARM微型电脑/IoT设备/嵌入式】树莓派安装失败sysstat,成功后还是无法使用sar,并报错:-bash:sar:command not found

1. 安装sysstat报错 安装&#xff1a; sudo apt-get install sysstat错误如下&#xff1a; dpkg: error processing package sysstat (–configure): installed sysstat package post-installation script subprocess returned error exit status 10 Errors were encountered…

1737C - Ela and Crickets

原题链接&#xff1a; Problem - 1737C - Codeforces 题目描述&#xff1a; The problem, which involves a non-standard chess pieces type that is described below, reads: given 33 white crickets on a n⋅nn⋅n board, arranged in an "L" shape next to ea…

详细讲解磁盘及文件系统管理(图例解析)

磁盘及文件系统管理详解 目前市场上主流的磁盘是机械式硬盘 u盘&#xff0c;光盘&#xff0c;软盘&#xff0c;硬盘&#xff0c;磁带 机械式硬盘 【硬盘内部由一个个同心圆组成】如下图&#xff1a; 硬盘内部所有盘片都固定在一根“轴”上&#xff0c;所以&#xff1a;所有…

Vant的List组件列表 滑动后不触底也发送请求的Bug

&#x1f4c3;目录跳转&#x1f4da;简介&#xff1a;&#x1f389;页面效果&#xff1a;&#x1f4ad;使用Float:&#x1f9d0; 问题&#xff1a;&#x1f52d;div 转为行内块 &#xff08;解决&#xff09;&#x1f3c6;总结&#xff1a;&#x1f4da;简介&#xff1a; Vant的…

docke入门基础知识

一、Docker 架构 Docker 包括三个基本概念: 镜像&#xff08;Image&#xff09;&#xff1a;Docker 镜像&#xff08;Image&#xff09;&#xff0c;就相当于是一个 root 文件系统。比如官方镜像 ubuntu:16.04 就包含了完整的一套 Ubuntu16.04 最小系统的 root 文件系统。 容…

html前端跨域问题的解决方案

前言&#xff1a; 在前端发出Ajax请求的时候&#xff0c;有时候会产生跨域问题&#xff0c;报错如下: Access to XMLHttpRequest at ‘http://127.0.0.1/api/post’ from origin ‘null’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is pre…

WINSOFT ComPort轻松连接到各种串行端口和连接设备

WINSOFT ComPort轻松连接到各种串行端口和连接设备 WINSOFT ComPort是一个Delphi库和CBuilder&#xff0c;通过USB端口连接到输入设备。该库提供CP210x、CP2130、CDC、FTDI、PL2303和CH34x设备。这些标准包括广泛的设备和电气从设备。例如&#xff0c;除了标准输入设备之外&…

Android App开发之利用Glide实现图片的三级缓存Cache讲解及实战(附源码 超详细必看 简单易懂)

需要图片集和源码请点赞关注收藏后评论区留言~~~ 一、利用Glide实现图片的三级缓存 图片加载框架之所以高效&#xff0c;是因为它不但封装了访问网络的步骤&#xff0c;而且引入了三级缓存的机制。具体来说&#xff0c;是先到内存中查找图片&#xff0c;找到了就直接显示内存图…

国内访问Github超级慢?那是你没有用我这个脚本。直接起飞。

导语 之前很多朋友咨询过国内访问Github较慢的问题&#xff0c;然后我一般让他们自己去知乎上找攻略&#xff0c;但今天我才发现网上竟然没有一个一键配置的脚本&#xff0c;一般都需要我们跟着教程一步步地去做才行。这也太麻烦了&#xff0c;于是自己动手写了个脚本&#xf…

2-1 C++类的转换函数与禁止隐士转换(explicit)

1. 转换函数与explicit关键字 1.1 转换函数 下述代码的第5行operator double()即是一个转换函数&#xff0c;通过这个函数&#xff0c;编译器可以在需要的情况下&#xff0c;直接将Fraction类型的对象转换为double类型。这个函数有两个特点&#xff1a;首先因为转换函数的返回…

FFplay文档解读-50-多媒体过滤器四

32.21 showspectrumpic 将输入音频转换为单个视频帧&#xff0c;表示音频频谱。 过滤器接受以下选项&#xff1a; size&#xff0c;s指定输出的视频大小。 有关此选项的语法&#xff0c;请查阅&#xff08;ffmpeg-utils&#xff09;视频大小语法。 默认值为4096x2048。 mod…

Spring Boot入门必会(基本介绍+依赖管理+自动装配)

目录 一.基础入门 1.Spring Boot 是什么? 2.SpringBoot 快速入门 2.1完成步骤 2.2快速入门小结 3.Spring SpringMVC SpringBoot 的关系 3.1梳理关系 3.2如何理解 -约定优于配置 二.依赖管理和自动配置 1.依赖管理 1.1 什么是依赖管理 1.2 修改自动仲裁/默认版本号 …

论文阅读-Federated Social Recommendation with Graph NeuralNetwork

基于图神经网络的联邦社交推荐 1. 引言 因此&#xff0c;针对社交推荐任务&#xff0c;我们设计了一个联邦学习推荐系统&#xff0c;该系统具有异构性、个性化和隐私保护要求&#xff0c;具有一定的挑战性。为此&#xff0c;设计了一个基于图神经网络(FeSoG)的联邦社交推荐框…

【RHCE】ansible的简单配置

目录 使用静态清单文件指定受管主机 定义主机清单 定义方式 使用静态主机清单指定受管主机&#xff08;默认&#xff09; 验证清单 第一种方式 第二种方式&#xff08;图表形式显示&#xff09; 选择主机和组&#xff1a; 1>匹配所有主机 2>匹配指定的主机或者主…

二叉树与树、森林之间的转换

关于树的概念 树可以称为特殊的森林 &#xff0c; 其中二叉树是树中一些节点度数最大为2 &#xff0c;并且分左右孩子的树 ● 二叉树很重要 • 结构简单 • 存储效率高 • 运算算法相对简单 • 任何森林、树都可以转换成二叉树 ● 讨论 • 二叉树 度为2 的树 ? 答: 树的度就是…

官方盘点 .NET 7 新功能

.NET 7 为C# 11/F# 7、.NET MAUI、ASP.NET Core/Blazor、Web API、WinForms、WPF 等应用程序带来了更高的性能和新功能。使用 .NET 7&#xff0c;您还可以轻松地将 .NET 7 项目容器化&#xff0c;在 GitHub 操作中设置 CI/CD 工作流&#xff0c;并实现云原生可观察性。欢迎下载…

java之Fork/Join框架

文章目录前言工作窃取算法Fork/Join框架的设计Fork/Join框架的异常处理Fork/Join框架的实现原理总结前言 Fork/Join框架是java7提供的一个用于执行并行任务的框架&#xff0c;是一个把大部分任务分割成若干个小任务&#xff0c;最终汇总每个小任务结果后&#xff0c;得到大任务…

4进程地址空间

文章目录前言1. 概念引入2. CPU和物理内存关系3.何为进程地址空间4. 为什么存在地址空间?前言 本节主要是讲解进程地址空间,区分和物理内存地址空间的差别,并且向读者解释四个疑问: 怎样验证地址空间的排布; 进程地址空间是什么; 进程地址空间和物理内存之间的关系; 为什么要…

[附源码]java毕业设计基于web的球类体育馆预定系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…