• 02-React组件与模块


    组件与模块

    前期准备

    安装React官方浏览器调试工具,浏览器扩展搜索即可
    在这里插入图片描述
    比如红色的React就是本地开发模式
    在这里插入图片描述
    在这里插入图片描述

    开启一个用React写的网站,比如美团
    此时开发状态就变成了蓝色
    在这里插入图片描述
    组件也能解析出来
    在这里插入图片描述

    何为组件&模块

    模块,简单来说就是JS代码分块了:
    在这里插入图片描述
    组件,简单来说就是把H5,CSS,JS,图像一些杂七杂八的整合到一个模块里:
    在这里插入图片描述


    模块化: 当应用的JS都以模块来编写,这个应用就是一个模块化的应用
    组件化: 当应用是以多组件的方式实现,这个应用就是一个组件化的应用

    函数式组件

    函数式组件,那就先写一个函数放在script标签里备着
    在这里插入图片描述
    写完就需要渲染,光一个函数在那放着肯定不行,所以用ReactDOM.render渲染到真实DOM上
    在这里插入图片描述
    打开页面,很明显,不行,因为React不会直接认识函数
    在这里插入图片描述
    之前我们说过,React自定义的组件首字母一定大写

    我们的函数式组件函数名就是组件的名字,所以组件名要大写,那么函数名也要大写!

    如果小写了就会出现这种错误,因为小写的标签会先去h5原生的标签找,所以会找不到
    在这里插入图片描述


    改一下,注意标签闭合
    在这里插入图片描述
    在这里插入图片描述

    执行ReactDom.render发生了什么

    • 1.React解析组件标签,然后找到了目标组件,比如上面的组件

    • 2.发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转为真实DOM,随后渲染到页面上

    类的基本知识复习

    原型链

    原型链:当在实例化的对象中访问一个属性时,首先会在该对象内部(自身属性)寻找,如找不到,则会向其__proto__指向的原型中寻找,如仍找不到,则继续向原型中__proto__指向的上级原型中寻找,直至找到或Object.prototype.__proto__为止(值为null),这种链状过程即为原型链。如下图所
    原型链的作用:查找对象的属性(方法)

    子类找不到就去父类去找

    代码

    在这里插入图片描述

    <!DOCTYPE html>
    <html lang="en">
        <head>
            <title>Hello React!</title>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1">
        </head>
        <body>
            <script type="text/javascript">
                // 用class创建类,类中无需定义变量,构造器只有在需要赋值的时候才需要定义
    
                class Person{
                    constructor(name,age){
                        //this指向当前实例对象
                        this.name=name;
                        this.age=age;
                        //当然可以在构造器里固定写值
                        this.school="尚硅谷"
                    }
                    // 类中定义方法不要用function,function就变成函数了
                    speak(){
                        console.log(`名字:${this.name},年龄:${this.age}`)
                    }
                    talk(){
                        console.log("people talk")
                    }
                }
                
                //继承
                class Student extends Person{
                    constructor(name,age,grande){
                        // 继承用super关键字复用父类的构造
                        // surper(父类构造参数)
                        super(name,age);
                        // 子类独有的构造要单独写
                        this.grande=grande;
                    }
                    speak(){
                        //直接调用构造的值即可
                        console.log(`名字:${this.name},年龄:${this.age},年级:${this.grande}`)
                    }
                    study(){
                        console.log("努力学习")
                    }
                }
    
                const p=new Person("张三","23");
                const student=new Student("李四","25","大三");
                console.log(p);
                p.speak();
                student.speak();
                student.study();
                //调用父类方法,先去原型链找,找到了就调用,找不到继续往下面的原型链找
                student.talk();
    
                
            </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

    类的创建

    类构造器

    子类

    子类构造器

    总结

    • 1.类中的构造器不是必须写的,只有要对实例做一些初始化的时候,比如添加指定属性时才写
    • 2.如果A类继承了B类,且A类写了构造器,那么A构造器中的super是必须调用的
    • 3.类中定义的方法都是放在了类的原型对象上的

    类组件

    创建类组件

    类组件,类名就是组件名

    // 类组件必须继承于React的Component
    // 创建类组件
    class MyComponet extends React.Component{
        // render是放在MyComponet的原型对象上面,供实例对象是用
        // render中的this是谁?
        //  实际上是指向MyComponet的实例对象,同时也是MyComponent组件实例对象
        render(){
            console.log("render中的this",this)
            return <div>aaa</div>
        }
    }
    // ReactDOM的render和类组件里的render没有关系,只是单纯的名字一样
    ReactDOM.render(<MyComponet/>,document.getElementById("test"))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    打印的this对象:
    在这里插入图片描述


    执行代码,成功渲染

    ReactDOM.render如何渲染类组件

    ReactDOM.render(,document.getElementById("真实DOM的id"))执行时发生了什么

    • 1.React解析对象,通过找到同名 MyComponet class组件
    • 2.发现组件是类组件,随后自动将MyComponet类new出来对象,并且自动调用render方法
    • 3.将render返回的虚拟DOM转换为真实DOM,随后页面显示

    类组件的实例核心属性

    注意,我们这里说的是,类组件实例的核心属性

    也就是说,只有实例对象才有属性

    所以这里直接pass掉函数组件,函数组件的是无法创建对象的,只有类组件才能创建实例对象

    给Java程序员一个类比
    比如定义一个方法 public void test(){},方法是没办法创建对象的。(类比函数组件)
    但是定义一个类,public class Test{},类是可以创建对象的。(类比类组件)
    
    • 1
    • 2
    • 3

    在这里插入图片描述

    核心属性1:state

    组件的状态里面存着数据,数据的改变驱动页面展示
    在这里插入图片描述

    state例子

    通过state对画面进行动态控制

    <script type="text/babel">
        // 借助构造器,对state设置值
        class MyComponet extends React.Component{
            constructor(props){
                // 由于继承于React.Component
                // 所以用构造器设置state值时,必须调用父类构造器(类组件语法,子类使用构造器时必须先用super调用父类构造器)
                // 参数固定为属性props
                super(props);
                // 对当前对象state进行修改,注意是state,而不是states,这两个是不同的东西
                // state在设置时需要用{key1:value1,key2:value2,...}这样来set
                this.state={isHot:true}
            }
            render() {
                console.log(this);
                return (
                    <div>
                        今天天气{this.state.isHot?'炎热':'凉爽'}
                    </div>
                );
            }
        }
        // ReactDOM的render和类组件里的render没有关系,只是单纯的名字一样
        ReactDOM.render(<MyComponet/>,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

    如图,state已经成功set上值
    在这里插入图片描述
    在这里插入图片描述


    利用React工具看一下组成:
    在这里插入图片描述

    将组件绑定事件

    先回顾一下原生的几种绑定方式
    在这里插入图片描述

    注意,H5中一些原生的属性在React中已经完成了重写
    比如:
    onclick在React中变成了onClick
    onblur变成了onBlur
    class变成了className
    
    • 1
    • 2
    • 3
    • 4
    • 5

    如果写错了在控制台就会有提示
    在这里插入图片描述

    最后引入React的推荐写法

    先分享一个错误写法

     <script type="text/babel">
         // 借助构造器,对state设置值
         class MyComponet extends React.Component{
             constructor(props){
                 super(props);
                 this.state={isHot:false}
             }
             render() { 
                 return (
                 //  
    这种函数用字符串的形式肯定是不可以的 <div onClick={test()}> //换用表达式写法 今天天气{this.state.isHot?'炎热':'凉爽'} </div> ); } } // ReactDOM的render和类组件里的render没有关系,只是单纯的名字一样 ReactDOM.render(<MyComponet/>,document.getElementById("test")) function test(){ console.log("OK") }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    这里会造成一个问题,就是把 表达式传给了onClick()事件
    也就是onClick传了个函数表达式进去,而不是函数名

    而这个函数表达式是个undefined的返回值,就会导致onClick事件触发函数后接收了undefined,如果是有小括号这样传值,会触发一次回调,onClick在接收到undefined之后就会不动了,导致后续事件都失效

    <div onClick={test()}></div> //表达式
    <div onClick={test}></div> //函数名
    
     function test(){
         console.log("OK")
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    所以主要是记住,**如果要绑定事件,传一个函数名就行**
    有的小伙伴可能会问,那这样怎么传参?
    答案是箭头函数,用箭头函数传参
    
    • 1
    • 2
    • 3

    最后改成正确答案

    <script type="text/babel">
        // 借助构造器,对state设置值
        class MyComponet extends React.Component{
            constructor(props){
                super(props);
                this.state={isHot:false}
            }
            render() { 
                return (
                    <div onClick={test}>
                        今天天气{this.state.isHot?'炎热':'凉爽'}
                    </div>
                );
            }
        }
        
        // ReactDOM的render和类组件里的render没有关系,只是单纯的名字一样
        ReactDOM.render(<MyComponet/>,document.getElementById("test"))
        function test(){
            console.log("OK")
        }
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    在这里插入图片描述

    将函数优化进组件里

    理论上,函数和组件应该是融为一体的,不应该相互独立

    所以将function函数放进类组件,只保留函数名+函数体即可
    在这里插入图片描述

    运行代码会报错,因为onClick没有指定this的指向
    在这里插入图片描述
    在这里插入图片描述

    如果放入类组件内部,则意味着属于这个类,所以onClick回调时需要用this指向,指向当前实例

    修改指向,不再报错
    在这里插入图片描述

    解决类中的this指向问题

    <script type="text/babel">
      // 借助构造器,对state设置值
       class MyComponet extends React.Component{
           constructor(props){
               // 由于继承于React.Component
               // 所以用构造器设置state值时,必须调用父类构造器(类组件语法,子类使用构造器时必须先用super调用父类构造器)
               // 参数固定为属性props
               super(props);
               // 对当前对象state进行修改,注意是state,而不是states,这两个是不同的东西
               this.state={isHot:false}
               // 解决test中的指向问题,这时如果再在test中输出,就不再是undefined
               this.test=this.test.bind(this)
           }
    
           test(){
               console.log("OK")
               // test 函数放在哪? 放在MyComponet的原型对象上,供实例使用
               // 由于test方法是onClick的回调,所以就不能用对象实例调用,直接调用即可
               // 类方法中默认开启了局部的严格模式,所以此时如果输出this对象,即为undefined
               console.log(this)
           }
    
           render() { 
               return (
                   <div onClick={this.test}>
                       今天天气{this.state.isHot?'炎热':'凉爽'}
                   </div>
               );
           }
       }
       
       // ReactDOM的render和类组件里的render没有关系,只是单纯的名字一样
       ReactDOM.render(<MyComponet/>,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

    状态不可直接更改,要用setState

    反例:直接操作状态(state)来实现boolean值切换
    在这里插入图片描述
    在这里插入图片描述


    由此可见,state是不可以直接修改的!所以要借助对象内部的API来修改

    打印一下对象,看看原型链,找到最顶层的ReactComponent的原型对象,类似于Object的性质
    在这里插入图片描述
    所以我们如果想修改,就只能用setState({key:value})
    调用时:

    this.setState({key:value})
    
    • 1
    setState是一个合并操作

    比如我定义了一个state

    this.state({"key1":value1},{"key2":value2})
    
    • 1

    如果此时,我对key1进行修改操作,此时仅仅只针对key1进行覆盖操作,不会影响key2以及key2的值。

    this.setState({"key1":test})
    
    • 1

    那么此时的state就会变成

    this.state({"key1":test},{"key2":value2})
    
    • 1

    如果再来一个key3,就再合并进去,这就是所谓的合并操作

    组件各部分渲染次数

    构造器 constructor => 有几个ReactDOM.render提及了这个组件,就构造几次,一般来说是一次
    ReactDOM.render(,document.getElementById("test"))

    普通函数 test => 有几次事件触发,或者被调用,这个函数就调用几次

    渲染 render => 1+n次,1是初始化时的渲染,n是state的修改次数

    <script type="text/babel">
    	class MyComponet extends React.Component{
    	    constructor(props){
    	    	// 构造器 
    	    }
    	    test(){
    	    	// 普通函数
    	        console.log(this)
    	        this.setState({"key1":value1},{"key2":value2})
    	    }
    	    render() { 
    	    	// render函数负责渲染 
    	        return (
    	            <div onClick={this.test}>
    	                今天天气{this.state.isHot?'炎热':'凉爽'}
    	            </div>
    	        );
    	    }
    	}
    	
    	ReactDOM.render(<MyComponet/>,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

    setState的简写方式

    首先看看类中的一个知识,就是定义一个固定的值是不需要去像函数一样,定义变量类型变量名一连串操作… 直接赋值即可

    构造器中定义固定值,和构造器外类内去定义效果一样

    class MyComponet extends React.Component{
        constructor(props){
            this.name=name
            this.state={isHot:false}
            //构造器定义固定值
            this.age=25
        }
        // 直接定义值是一样的,类中不需要定义变量,直接赋值即可
        age=25
    	// 就连state也不用在构造器去写了,直接定义即可
    	state={isHot:false}
        render() { 
            return (
                <div onClick={this.test}>
                    今天天气{this.state.isHot?'炎热':'凉爽'}
                </div>
            );
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    自定义方法改写

    为了解决类中方法的this指向问题,而采用了赋值语句接收箭头函数,如果是箭头函数,那么里面的this就会自动向外寻找,寻找其再外面一层的this,所以函数的再外一层的指向就是类,随即解决this指向问题。

    test作为变量接收箭头函数的返回值
    这样就相当于把箭头函数,赋值给了对象的属性上,
    在属性调用的时候,相当于通过属性调用函数
    属性一定有this,所以就解决了this的指向问题
    
    • 1
    • 2
    • 3
    • 4
    实际开发过程中,自定义方法要用箭头函数,而不是这种写法
      test(){
          console.log(this)
          this.state({"key1":value1},{"key2":value2})
      }
    箭头函数,test作为变量接收箭头函数的返回值,这样就相当于把箭头函数,赋值给了对象的属性上,这样在属性调用的时候,相当于通过属性调用函数,这样就解决了this指向问题
      test = ()=>{
          console.log(this)
          this.state({"key1":value1},{"key2":value2})
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    注意:类里面不支持这种箭头函数写法,必须去用值接收

      test ()=>{
          console.log(this)
          this.state({"key1":value1},{"key2":value2})
      }
    
    • 1
    • 2
    • 3
    • 4

    state总结


    理解:

    1.state是组件对象最重要的属性,值是对象({"key1":value1},{"key2":value2})

    2.组件被称为状态机,通过更新组件的state来更新对应的页面显示(重新渲染组件)


    强烈注意:

    1.组件中render方法中的this为组件实例对象

    2.组件自定义的方法中this为undefined,如何解决?

    • a. 强制绑定this:通过函数对象的bind()方法来创建一个新函数
    • b. 用属性接收一个箭头函数

    3.状态数据,不能直接修改或更新,需要通过特定API来处理

    核心属性2:props

    前面看state只是类组件内部的东西,类组件内部可以提前准备一些数据用

    回顾js展开运算符

    首先回顾一下js的展开符号 ...

    首先以打印一个数组为例:

     <script type="text/javascript">
         let a=[1,2,3]
         console.log(a)
     </script>
    
    • 1
    • 2
    • 3
    • 4

    这种的输出就是结果就是这样,是叠在一起的
    ,


    但如果是用了展开符号,再打印,数组内部结构就会被展开铺开

     <script type="text/javascript">
         let a=[1,2,3]
         console.log(...a)
     </script>
    
    • 1
    • 2
    • 3
    • 4

    在这里插入图片描述

    展开运算符的其他用法

    可以用作两个数组连接

    反例:
    在这里插入图片描述
    用展开符连接
    在这里插入图片描述

    注意:展开符无法直接展开对象
    比如,直接展开a对象会报错
    在这里插入图片描述
    但是利用展开符复制对象是可以的
    在这里插入图片描述
    点击这里查看MDN官方文档对于展开符的解释

    在这里插入图片描述

    展开复制过程中直接修改某个属性

    如果对于某个属性不满意,当然可以直接在复制过程中直接修改

    let 新对象 = {...展开源对象,key:value,key2:value2......}
    
    • 1

    在这里插入图片描述

    展开符就复习到这,开始进入React部分


    React Props传参

    如果总是用对象中的state,就好像只能内部操作属性,无法获取外部的值,此时就得用Props属性,来实现从外部对类标签进行传值的操作

    直接在标签上,以key=value的形式传值,注意,这里value先按照字符串来填写,写数字会报错,后面会解释为什么
    在这里插入图片描述
    但是不难发现,如果写了n多个属性,就会很繁琐,所以引入展开符,效果一样,更加方便

    注意,传入对象和被使用的对象key之间,key要匹配上,不然key都对不上了,肯定找不到value了
    在这里插入图片描述
    这里有一个非常有意思的点,之前上面刚刚才说过,对象不可以用展开符直接展开,这里就用到了展开符去传参。

    因为这个是在React中的特殊语法,标签上可以这么去搞。有React依赖+babel就可以这么用

    标签传值数字问题

    直接传一个数字是不行的,因为React默认value是有数据类型的,双引号默认是String。
    在这里插入图片描述
    所谓数据类型,只有在JS中才有,既然是这样,我们传一个JS代码就可以了,即传入参数用{}包裹,这样数字就会被识别为Number类型,自然不会有问题
    在这里插入图片描述

    对象.propTypes和PropTypes的区别

    就和Java一样,React传参编译没有明显提示

    • 问题1:如果我希望传一个String,但是方法调用者传了一个Number就会有问题
    • 问题2:某个属性没有值,那我希望给一个默认值,这个默认值该如何设置

    可以看下面两个:

    属性类型约束

    React16以前的类型指定
    对象.propTypes来写规则代码

    React.PropTypes.类型来指定类型

    在这里插入图片描述
    React16及以后,因为React对象过于臃肿因此对象类型不再集成在React中,所以就引入了prop-types.js来做类型指定
    在这里插入图片描述
    直接用PropTypes.类型即可,测试可以正常渲染
    在这里插入图片描述
    甚至可以传函数进去,注意函数的关键字是func而不是function,这么写是避免重名
    在这里插入图片描述

    属性默认值

    简单指定一下即可,不是很难

     MyComponet.defaultProps={
         // 如果不传值,则默认张三
         name:"张三",
         // 如果不传值,则默认18
         age:18,
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    将默认属性值和属性约束集成在类中

    前面我们看到,默认属性值和属性约束都写在类的外面,这就违反了我们类组件的初衷,所以我们要把这个写在类内部

    注意,是类的内部,render的外部
    直接用static来修饰即可,同时去掉 类名称调用

    在这里插入图片描述

    props是只读的

    直接修改props是会报错的,因为其是只读的
    在这里插入图片描述
    补充一下
    在这里插入图片描述

    函数式组件使用Props

    首先,函数式组件,是没有实例对象的
    所以,对象的三大属性,就有两个废掉了用不了,唯有props可以留下,因为比较特殊,在函数组件需要传参使用。
    除此以外,像static这种类才会有的关键字就用不了了

    以下是例子:

    在这里插入图片描述

    核心属性3:ref

    可以理解为标签用来标识自己用的(类似于id)

    组件内的标签可以定义ref属性来标识自己

    每个标签上可以定义ref,对象会将这些ref收集起来到对象身上,对象身上有refs的属性,代表多个标签的ref被收集起来,我们可以通过这些ref来获取真实DOM

    传统获取DOM的方式

     class MyComponet extends React.Component{
         showDate=()=>{
    //这种传统形式获取太麻烦,因为React是操作DOM的,所以我们借助React的ref来获取真实DOM
             const inputDiv=document.getElementById("input1")
             console.log(inputDiv.value)
         }
          render(){
                 return (
                     <div>
                         <input id="input1" type="text"></input>
                     </div>
                 )
             }
         }
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    下面介绍React中的ref,ref有很多形式,具体可以看下面的介绍

    字符串形式的ref(弃用)

    React已弃用,一些问题指的是效率问题
    在这里插入图片描述

    字符串形式的ref指的是标签里的ref="字符串"
    接受了一个字符串,所以是字符串形式的ref

    注意,写在标签里的是ref,在标签外收集的时候是用this.refs来进行收集,最后通过解构的方式拿到key

    <!DOCTYPE html>
    <html lang="en">
        <head>
            <title>Hello React!</title>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1">
        </head>
        <body>
            <div id="test"></div>
        </body>
        <script type="text/javascript" src="../js/react.development.js"></script> <!--React核心的库-->
        <script type="text/javascript" src="../js/react-dom.development.js"></script><!--React操作Dom相关库-->
        <script type="text/javascript" src="../js/babel.min.js"></script><!--JSX转换成JS用的依赖,因为浏览器不认识JSX,只认识JS-->
        <script type="text/babel">
            class MyComponet extends React.Component{
                showData=()=>{
                    //这种传统形式获取太麻烦,因为React是操作DOM的,所以我们借助React的ref来获取真实DOM
                    const inputDiv=document.getElementById("input1")
                    alert(inputDiv.value)
                }
                showData2=()=>{
                    // 通过React对象来操作,通过refs属性获取完之后解构
                    // 这里解构是依靠input2的ref名字来进行解构的
                    const {input2}=this.refs;
                    alert(input2.value)
                }
                render(){
                    return (
                        <div>
                            <input id="input1" type="text" placeholder="点击按钮弹出左侧数据"></input>
                            <button onClick={this.showData2}></button>
                            <input ref="input2" onBlur={this.showData2} type="text" placeholder="获得焦点提示数据"></input>
                        </div>
                    )
                }
            }
    
            ReactDOM.render(<MyComponet/>,document.getElementById("test"))
        </script>
    
    </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

    回调函数形式的ref

    主要是标签上的ref参数,通过回调函数的形式,将当前节点通过箭头函数传传递到当前对象上

    其中,current代表传入的DOM,即当前组件this.test=current代表了在对象上创建一个test属性,并且将当前传入的current DOM给挂载到对象上

    这里的this指向是谁? 因为这个标签是在render里面的,所以ref里的回调函数,会向外扩散寻找对象,即找到类组件的对象

    <input ref={(current)=>{this.test=current}}><input/>
    
    • 1
    提示,ES6语法:
                const {test}=this
    这两个写法并不等价
    		    const {test}=this.test
    
    • 1
    • 2
    • 3
    • 4
    在JavaScript ES6中,你可以使用解构赋值(destructuring assignment)来从对象中提取属性。
    当你写 const {test}=this 时,你是在从 this 对象中提取 test 属性,并将其赋值给 test 变量。
    
    然而,当你尝试 const {test}=this.test 时,你实际上是在尝试从 this.test 这个值(而不是对象)中提取属性。
    如果 this.test 是一个对象,那么这个表达式就会尝试从该对象中提取 test 属性。
    如果 this.test 不是一个对象,那么这个表达式就会失败,因为它无法从非对象值中提取属性。(属性自己是不能再拿出一个属性的,只有对象才能拿出属性)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    代码示例:

    <script type="text/babel">
        class MyComponet extends React.Component{
            showData=()=>{
                // 直接解构对象属性,获取DOM
                const {test}=this
                // 获取属性
                alert(test.value)
            }
            render(){
                return (
                    <div>
                        <input ref={(current)=>this.test=current} type="text" placeholder="点击按钮弹出左侧数据"></input>
                        <button onClick={this.showData}>点击触发</button>
                    </div>
                )
            }
        }
    
        ReactDOM.render(<MyComponet/>,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

    回调ref的执行次数问题

    回调ref的次数在组件被更新的时候会触发两次,在组件第一次创建的时候只触发一次

    为什么更新的时候是触发两次

    • 第一次是为了在更新之前,把上一次的处理后的属性内容都清空掉,传一个null进去。(清空的动作)
    • 第二次就是正常的触发,该正常传DOM就传DOM。(更新的动作)

    多的代码就不放了,这个不必纠结,直接正常用回调函数即可,不必纠结两次回调
    在这里插入图片描述

    createRef 官方推荐

    这个是React官方大力推荐的ref的创建方式,主要指的是React.createRef()函数

    React.createRef(),调用后可以返回一个容器,该容器可以存储被ref标识的节点,该容器是“调用ref的标签专属的”,如果后续还有组件用了同名的ref属性,就会覆盖之前的ref

    // 直接就可以挂载到对象属性上
    对象属性=React.createRef()
    
    • 1
    • 2

    在这里插入图片描述

    除此以外,React官方是不推荐去多用ref,尽量多用state来管理属性,ref主要是与DOM绑定在一起

    在这里插入图片描述

    JSX的注释问题

    JSX的注释需要用花括号括起来

    括起来之后,就可以在内部写JS的注释了,因为被识别为JS语句了

    比如:
    在这里插入图片描述

  • 相关阅读:
    math_消除根式:椭圆的标准式方程推导&坐标系平移&整理多项式
    python读取PDF文件并做词云可视化
    HTML+CSS篮球静态网页设计(web前端网页制作课作业)NBA杜兰特篮球运动网页
    【2023牛客多校训练营10】L Grayscale Confusion
    猿创征文 | H5API之websocket和地理位置的获取
    激光数据去畸变
    leetcode 1457. Pseudo-Palindromic Paths in a Binary Tree(二叉树中的伪回文路径)
    7-2 友元类Cvector 武汉理工大学
    使用系统识别应用程序识别低阶传递函数(过程模型)
    (十) 共享模型之内存【有序性】
  • 原文地址:https://blog.csdn.net/weixin_46906696/article/details/133893237