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


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

    对照代码查看笔记: 代码详情

    • 有详细的ReadMe文档
    • 欢迎star+fork到自己的仓库
    • 后续将进行todolist案例的详细讲解:vue2,vue3,react

    一、 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
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    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
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    1-3-1 函数的柯里化

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

    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>Documenttitle>
      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>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66

    二、生命周期

    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>Documenttitle>
      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>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84

    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>Documenttitle>
      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
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    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>Documenttitle>
      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>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    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没有问题
  • 相关阅读:
    Python 类型注解
    在D中如何64位mulhi
    【Azure 应用服务】NodeJS Express + MSAL 应用实现AAD集成登录并部署在App Service Linux环境中的实现步骤
    MySQL-锁
    JWT登录验证前后端设计与实现笔记
    猿创征文 | [云原生]为微服务保驾护航之链路跟踪skywalking保姆级搭建教程
    如何对低代码/无代码平台进行分类?
    [管理与领导-122]:IT人看清职场中的隐性规则 - 18- 一半佛一半魔,一半君子一半小人,阴阳互转,生生不息,儒、释、道、法,一个不能少
    分库分表索引设计:二级索引、全局索引的最佳设计实践
    《docker高级篇(大厂进阶):4.Docker网络》包括:是什么、常用基本命令、能干嘛、网络模式、docker平台架构图解
  • 原文地址:https://blog.csdn.net/hannah2233/article/details/127853625