React:二、react面向组件编程

React:二、react面向组件编程

1 前言

1.1 React开发者工具调试

可先在浏览器的扩展处安装react_dev_tools,如microsoft-edge、谷歌浏览器等等。

如edge浏览器打开F12:
在这里插入图片描述
2 组件类别

2.1 函数式组件

tips:vscode快捷输入html的初始模板:!+tab

2.1.1 React的函数式组件,因标签必须闭合,且小写的为html的原生标签,故而React的函数式组件必须大写字母开头,因此函数名也是大写字母开头:

<!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>
    <script src="../node_modules/react/umd/react.development.js"></script>
    <script src = "../node_modules/react-dom/umd/react-dom.development.js"></script>
    <script src="../node_modules/babel-standalone/babel.min.js"></script>
</head>
<body>
    <div id="test"></div>
    <script type="text/babel">
        function XiaoXuComponent(){
            return <h1>函数式组件,简单的组件</h1>
        }
        ReactDOM.render(<XiaoXuComponent/>,document.getElementById("test"))
    </script>
</body>
</html>

在这里插入图片描述
2.1.2 函数式组件中的this是undefined,因为babel编译后,ES6->ES5,开启了严格模式use strict:
在这里插入图片描述

<!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>
    <script src="../node_modules/react/umd/react.development.js"></script>
    <script src = "../node_modules/react-dom/umd/react-dom.development.js"></script>
    <script src="../node_modules/babel-standalone/babel.min.js"></script>
</head>
<body>
    <div id="test"></div>
    <script type="text/babel">
        function XiaoXuComponent(){
            console.log("函数中this:"+this)//this:undefined,babel编译后开启了严格模式
            return <h1>函数式组件,简单的组件</h1>
        }
        ReactDOM.render(<XiaoXuComponent/>,document.getElementById("test"))
    </script>
    <!-- 
        1.react解析组件标签,找到XiaoXuComponent组件
        2.发现组件是函数定义,随后调用该函数,将返回的虚拟DOM转为真实DOM
     -->

</body>
</html>

2.2 类式组件

2.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>
    <script src="../node_modules/react/umd/react.development.js"></script>
    <script src = "../node_modules/react-dom/umd/react-dom.development.js"></script>
    <script src="../node_modules/babel-standalone/babel.min.js"></script>
</head>
<body>
    <script type="text/babel">
        class Xiaoxu{
            constructor(name,age){
                this.name = name
                this.age = age
            }
            // talk方法放在类的原型对象上(prototype),供实例使用
            talk(){
                console.log(`我是${this.name},我已经${this.age}岁了`)
            }
        }

        class Xiaoxu2 extends Xiaoxu{
            constructor(name,age,hobby){
                super(name,age)
                this.hobby = hobby
            }
            // 类似java,子类重写父类的方法,那么原型链调用Xiaoxu2的实例对象的talk方法时,
            // 优先调用子类重写的父类的方法,如果子类没有该方法,就去父类中找到并执行
            // talk(){
            //     console.log(`我是${this.name},我已经${this.age}岁了,爱好是${this.hobby}`)
            // }
        }
        const x = new Xiaoxu("小李",27);
        const x2 = new Xiaoxu2("小薛",32,"羽毛球");
        console.log(x)
        console.log(x2)
        x.talk()
        x2.talk()
    </script>
    <!-- 
        1.类中的构造器不是必须写的,要对实例初始化一些操作,如添加指定属性时才写
        2.如果A类继承了B类,且A类中写了构造器,那么A类构造器中的super是必须要调用的,且必须在
        最开始调用,否则报错this不能在super前面...的错误(同java)
        3.类中所定义的方法,都是放在了类的原型对象上,供实例去使用
     -->
</body>
</html>

如下可知,Xiaoxu类的实例对象的prototype原型对象上,有talk方法,而Xiaoxu2类中因为没有定义talk方法,故而原型对象prototype上没有talk方法,仅有constructor实例方法(XIaoxu2的),在Xiaoxu2类的原型对象的原型对象上(原型链中查找),就有父类Xiaoxu的talk方法,故而调用Xiaoxu2实例对象的talk方法,实际上是调用父类的talk方法,思维上与java一致:
在这里插入图片描述
2.2.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>
    <script src="../node_modules/react/umd/react.development.js"></script>
    <script src = "../node_modules/react-dom/umd/react-dom.development.js"></script>
    <script src="../node_modules/babel-standalone/babel.min.js"></script>
</head>
<body>
    <div id="test"></div>
    <script type="text/babel" >
        class XiaoXuComponent extends React.Component{
            render(){
                console.log("我是类式组件的this(是XiaoXuComponent的实例对象(或者说组件对象)):")
                console.log(this)
                return <h1>类式组件,复杂的组件</h1>
            }
        }
    ReactDOM.render(<XiaoXuComponent/>,document.getElementById("test"))
    /* 
    1.react解析组件标签<XiaoXuComponent/>,找到组件XiaoXuComponent
    2.发现组件是使用类定义的,new 出该类的实例对象,并通过该实例调用到原型对象上的render方法
    3.将render返回的虚拟DOM转换成真实DOM,呈现在页面上
    */
    </script>
</body>
</html>

在这里插入图片描述
2.3 组件核心属性

react中针对class组件,用到如下3种核心组件属性:

2.3.1 1/3:state

(1) state的值是对象(多个key-value组成的对象)

(2) 通过更新组件的state,来更新对应的页面显示(重新渲染组件)

注意:

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

(2) 组件自定义的方法中this为undefined,如何解决?
a.强制绑定this,通过函数对象的bind()
b.箭头函数

(3) 状态数据,不能直接修改或更新

2.3.1.1 state的简单使用:

<!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>

    <script src="../node_modules/react/umd/react.development.js"></script>
    <script src = "../node_modules/react-dom/umd/react-dom.development.js"></script>
    <script src="../node_modules/babel-standalone/babel.min.js"></script>
</head>
<body>
    <div id="test"></div>
    <script type="text/babel" >
        class XiaoXuComponent extends React.Component{
            constructor(prop){
                super(prop)
                this.state = {isHappy:true,drinks:"芒果饮料"}
            }
            render(){
                const {isHappy,drinks} = this.state
                return <h1 onClick = {cli}>今天我很{isHappy?"开心":"悲伤"},我爱喝{drinks}</h1>
            }
        }

    /* 
    注意:jsx语法中,点击事件onClick是小驼峰写法,且加上{}才能放入表达式,并且cli函数只能写
    cli,不能写cli(),因为只是把函数调用赋值给onClick,如果加cli(),那么不点击也会执行函数
    */
    function cli(){
        console.log("我被点击了")
    }

    ReactDOM.render(<XiaoXuComponent/>,document.getElementById("test"))

    /* 
    以下1,2,3种方式均可:

    const btn = document.getElementById("test")
    btn.addEventListener('click',()=>{
        alert('')
    })

    const btn2 = document.getElementById("test")
    btn2.onclick = () =>{
        alert('')
    }

    <button id = "test1" onclick = "demo()">哈哈</button>

    function demo(){
        alert('')
    }

    */
    </script>
</body>
</html>

在这里插入图片描述
修改function如下:
在这里插入图片描述
点击报错:
在这里插入图片描述
因为最外层的函数,在babel转换为严格模式(use strict)后,会禁止自定义的函数指向window,所以cli函数中的this是undefined。

2.3.1.2 修改function中this的指向:

<!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>

    <script src="../node_modules/react/umd/react.development.js"></script>
    <script src = "../node_modules/react-dom/umd/react-dom.development.js"></script>
    <script src="../node_modules/babel-standalone/babel.min.js"></script>
</head>
<body>
    <div id="test"></div>
    <script type="text/babel" >
        let that
        class XiaoXuComponent extends React.Component{
            constructor(prop){
                super(prop)
                this.state = {isHappy:true,drinks:"芒果饮料"}
                that = this
            }
            render(){
                const {isHappy,drinks} = this.state
                return <h1 onClick = {cli}>今天我很{isHappy?"开心":"悲伤"},我爱喝{drinks}</h1>
            }
        }

    /* 
    注意:jsx语法中,点击事件onClick是小驼峰写法,且加上{}才能放入表达式,并且cli函数只能写
    cli,不能写cli(),因为只是把函数调用赋值给onClick,如果加cli(),那么不点击也会执行函数
    */
    function cli(){
        console.log("我被点击了")
        const {isHappy,drinks} = that.state
        console.log(isHappy)
    }

    ReactDOM.render(<XiaoXuComponent/>,document.getElementById("test"))

    /* 
    以下1,2,3种方式均可:

    const btn = document.getElementById("test")
    btn.addEventListener('click',()=>{
        alert('')
    })

    const btn2 = document.getElementById("test")
    btn2.onclick = () =>{
        alert('')
    }

    <button id = "test1" onclick = "demo()">哈哈</button>

    function demo(){
        alert('')
    }

    */
    </script>
</body>
</html>

点击效果正确:
在这里插入图片描述
2.3.1.3 将function移动到类中:

上述方式虽然function的this指向正确了,但是代码很繁琐,简洁来说,应该类就是单纯的创建组件的操作,然后ReactDOM负责渲染组件就好了,因此做如下的修改:

<!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>

    <script src="../node_modules/react/umd/react.development.js"></script>
    <script src = "../node_modules/react-dom/umd/react-dom.development.js"></script>
    <script src="../node_modules/babel-standalone/babel.min.js"></script>
</head>
<body>
    <div id="test"></div>
    <script type="text/babel" >
        class XiaoXuComponent extends React.Component{
            constructor(prop){
                super(prop)
                this.state = {isHappy:true,drinks:"芒果饮料"}
            }
            render(){
                const {isHappy,drinks} = this.state
                return <h1 onClick = {this.cli}>今天我很{isHappy?"开心":"悲伤"},我爱喝{drinks}</h1>
            }
            cli(){
                console.log("我被点击了")
                const {isHappy,drinks} = this.state
                console.log(isHappy)
            }   
        }

    /* 
    注意:jsx语法中,点击事件onClick是小驼峰写法,且加上{}才能放入表达式,并且cli函数只能写
    cli,不能写cli(),因为只是把函数调用赋值给onClick,如果加cli(),那么不点击也会执行函数
    */

    ReactDOM.render(<XiaoXuComponent/>,document.getElementById("test"))

    /* 
    以下1,2,3种方式均可:

    const btn = document.getElementById("test")
    btn.addEventListener('click',()=>{
        alert('')
    })

    const btn2 = document.getElementById("test")
    btn2.onclick = () =>{
        alert('')
    }

    <button id = "test1" onclick = "demo()">哈哈</button>

    function demo(){
        alert('')
    }

    */
    </script>
</body>
</html>

但是点击还有报错:
在这里插入图片描述
render中的this没有问题,是因为React渲染时,根据标签组件< XiaoXuComponent/>先new一个实例,然后用实例调用的render()方法,所以指向无误;但是类中自定义的方法cli,并不是XiaoXuComponent类的实例去调用的,故而this指向有问题。

上述可理解为:类中的局部方法都开启了严格模式(use strict),和babel无关,因此类的局部方法中,this均是undefined。

2.3.1.4 修改类中function的this指向1:bind()方法

<!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>

    <script src="../node_modules/react/umd/react.development.js"></script>
    <script src = "../node_modules/react-dom/umd/react-dom.development.js"></script>
    <script src="../node_modules/babel-standalone/babel.min.js"></script>
</head>
<body>
    <div id="test"></div>
    <script type="text/babel" >
        class XiaoXuComponent extends React.Component{
            constructor(prop){
                super(prop)
                this.state = {isHappy:true,drinks:"芒果饮料"}
                this.cli = this.cli.bind(this)
            }
            render(){
                const {isHappy,drinks} = this.state
                return <h1 onClick = {this.cli}>今天我很{isHappy?"开心":"悲伤"},我爱喝{drinks}</h1>
            }
            cli(){
                console.log("我被点击了")
                const {isHappy,drinks} = this.state
                console.log(isHappy)
            }   
        }

    /* 
    注意:jsx语法中,点击事件onClick是小驼峰写法,且加上{}才能放入表达式,并且cli函数只能写
    cli,不能写cli(),因为只是把函数调用赋值给onClick,如果加cli(),那么不点击也会执行函数
    */

    ReactDOM.render(<XiaoXuComponent/>,document.getElementById("test"))

    /* 
    以下1,2,3种方式均可:

    const btn = document.getElementById("test")
    btn.addEventListener('click',()=>{
        alert('')
    })

    const btn2 = document.getElementById("test")
    btn2.onclick = () =>{
        alert('')
    }

    <button id = "test1" onclick = "demo()">哈哈</button>

    function demo(){
        alert('')
    }

    */
    </script>
</body>
</html>

注意:apply、call、bind均有改变this指向的作用,但是这里的jsx的{}中需要的是一个js表达式,应该返回的是函数对象,而非执行该函数,而apply和call都是直接执行函数,只有bind除了改变this的指向外,还会返回函数本身(也就是 func.bind(this)()才会执行该函数),故而此处只能使用bind。且此处调用的cli方法,是自身的,也就是this.cli = this.cli.bind(this)中的cli,而非原型对象上的cli(因为自身有了,优先使用自身的cli方法),可如下验证:
在这里插入图片描述
在这里插入图片描述
2.3.1.5 修改类中function的this指向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>

    <script src="../node_modules/react/umd/react.development.js"></script>
    <script src = "../node_modules/react-dom/umd/react-dom.development.js"></script>
    <script src="../node_modules/babel-standalone/babel.min.js"></script>
</head>
<body>
    <div id="test"></div>
    <script type="text/babel" >
        class XiaoXuComponent extends React.Component{
            constructor(prop){
                super(prop)
                this.state = {isHappy:true,drinks:"芒果饮料"}
                // this.cli = this.cli.bind(this)
            }
            render(){
                const {isHappy,drinks} = this.state
                return <h1 onClick = {this.cli}>今天我很{isHappy?"开心":"悲伤"},我爱喝{drinks}</h1>
            }
            cli= ()=>{
                console.log("我被点击了")
                // 注意!!!状态(state)不可以直接更改,要借助内置的API去更改
                const {isHappy,drinks} = this.state
                console.log(isHappy)
                console.log(this)
                // 下面是错误的
                // this.state.isHappy = !isHappy
                // 注意!!!状态(state)必须通过setState去修改(在React.Component
                // 的原型对象上有setState方法)
                this.setState({isHappy:!isHappy})
            }  
            // cli(){
            //     console.log("我被点击了")
            //     const {isHappy,drinks} = this.state
            //     console.log(isHappy)
            //     console.log(this)
            // }   
        }

    /* 
    注意:jsx语法中,点击事件onClick是小驼峰写法,且加上{}才能放入表达式,并且cli函数只能写
    cli,不能写cli(),因为只是把函数调用赋值给onClick,如果加cli(),那么不点击也会执行函数
    */

    ReactDOM.render(<XiaoXuComponent/>,document.getElementById("test"))

    /* 
    以下1,2,3种方式均可:

    const btn = document.getElementById("test")
    btn.addEventListener('click',()=>{
        alert('')
    })

    const btn2 = document.getElementById("test")
    btn2.onclick = () =>{
        alert('')
    }

    <button id = "test1" onclick = "demo()">哈哈</button>

    function demo(){
        alert('')
    }

    */
    </script>
</body>
</html>

并且只会替换掉原有的isHappy,而drinks:"芒果饮料"是不会更改的:
在这里插入图片描述
2.3.1.6 简化类组件写法

上述优选是选择箭头函数,而非bind()改变this,原因是如果类中自定义方法过多,那么constructor定义会显得臃肿,故而后续类组件中自定义方法,优先都使用箭头函数。且state的定义(初始化)也可进行省略,同时就可省略constructor的定义(也为初始化操作):

<!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>

    <script src="../node_modules/react/umd/react.development.js"></script>
    <script src = "../node_modules/react-dom/umd/react-dom.development.js"></script>
    <script src="../node_modules/babel-standalone/babel.min.js"></script>
</head>
<body>
    <div id="test"></div>
    <script type="text/babel" >
        class XiaoXuComponent extends React.Component{
            // 类中可以直接写赋值语句,如下含义为:给XiaoXuComponent类的实例对象增加一个属性,名state,值为对象:
            // {isHappy:true,drinks:"芒果饮料"}  故而constructor也可以一并省略
            state = {isHappy:true,drinks:"芒果饮料"}
            render(){
                const {isHappy,drinks} = this.state
                return <h1 onClick = {this.cli}>今天我很{isHappy?"开心":"悲伤"},我爱喝{drinks}</h1>
            }
            cli= ()=>{
                console.log("我被点击了")
                // 注意!!!状态(state)不可以直接更改,要借助内置的API去更改
                const {isHappy,drinks} = this.state
                console.log(isHappy)
                console.log(this)
                // 下面是错误的
                // this.state.isHappy = !isHappy
                // 注意!!!状态(state)必须通过setState去修改(在React.Component
                // 的原型对象上有setState方法)
                this.setState({isHappy:!isHappy})
            }  
        }
    ReactDOM.render(<XiaoXuComponent/>,document.getElementById("test"))
    </script>
</body>
</html>

如下可见,state直接成为了类的实例对象的属性:
在这里插入图片描述
2.3.2 2/3:props

2.3.2.1 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>

    <script src="../node_modules/react/umd/react.development.js"></script>
    <script src = "../node_modules/react-dom/umd/react-dom.development.js"></script>
    <script src="../node_modules/babel-standalone/babel.min.js"></script>
</head>
<body>
    <div id="test"></div>
    <script type="text/babel" >
        class XiaoXuComponent extends React.Component{
            state = {isHappy:true,drinks:"芒果饮料"}
            render(){
                console.log(this)
                console.log(this.props)
                const {name,price,stock} = this.props
                return (
                    <ul>
                        <li>水果名称:{name}</li>
                        <li>单价:{price}</li>
                        <li>库存:{stock}</li>
                    </ul>
                )
            }
            cli= ()=>{
            }  
        }
    ReactDOM.render(<XiaoXuComponent name="猕猴桃" price ={12.0} stock = {100} />,document.getElementById("test"))
    </script>
</body>
</html>

页面效果如下:
在这里插入图片描述
2.3.2.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>
    <script type = "text/javascript">
        let arr1 = [1,4,6]
        let arr2 = [2,9]
        let arr3 = [...arr1,...arr2]
        console.log(arr3)
        arr2[1] = 909
        console.log(arr2)
        console.log(arr3)

        function sum(...args){
            return args.reduce((oldValue,newValue)=>{
                return oldValue+newValue
            })
        }
        console.log(sum(5,7,1))

        let p = {name:"xiaoxu",age:"18"}
        let x = {...p}
        p.name = "xiaoxu111"
        console.log(p)
        console.log(x)

        let k = {...p,"hobby":"羽毛球","tool":"kkk"}
        console.log(k)
    </script>
</body>
</html>

如下可知,扩展运算符复制对象的时候,实际效果类似深拷贝(非浅拷贝:引用拷贝),更新原本的对象,不会更新扩展运算符复制的对象:
在这里插入图片描述
故而如上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>

    <script src="../node_modules/react/umd/react.development.js"></script>
    <script src = "../node_modules/react-dom/umd/react-dom.development.js"></script>
    <script src="../node_modules/babel-standalone/babel.min.js"></script>
</head>
<body>
    <div id="test"></div>
    <script type="text/babel" >
        class XiaoXuComponent extends React.Component{
            state = {isHappy:true,drinks:"芒果饮料"}
            render(){
                console.log(this)
                console.log(this.props)
                const {name,price,stock} = this.props
                return (
                    <ul>
                        <li>水果名称:{name}</li>
                        <li>单价:{price}</li>
                        <li>库存:{stock}</li>
                    </ul>
                )
            }
            cli= ()=>{
            }  
        }
    const obj = {name:"猕猴桃",price:13.0,stock:100}
    ReactDOM.render(<XiaoXuComponent {...obj} />,document.getElementById("test"))
    </script>
</body>
</html>

jsx中{}代表是js表达式,所以扩展运算符+obj,就能达到将key = value传入props的效果。

2.3.2.2 增加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>

    <script src="../node_modules/react/umd/react.development.js"></script>
    <script src = "../node_modules/react-dom/umd/react-dom.development.js"></script>
    <script src="../node_modules/babel-standalone/babel.min.js"></script>
    <!-- PropTypes:限制props -->
    <script src="../node_modules/prop-types/prop-types.js"></script>
</head>
<body>
    <div id="test"></div>
    <script type="text/babel" >
        class XiaoXuComponent extends React.Component{
            state = {isHappy:true,drinks:"芒果饮料"}
            render(){
                console.log(this)
                console.log(this.props)
                const {name,price,stock} = this.props
                return (
                    <ul>
                        <li>水果名称:{name}</li>
                        <li>单价:{price}</li>
                        <li>库存:{stock}</li>
                    </ul>
                )
            }
            cli= ()=>{
            }  
        }

        XiaoXuComponent.propTypes = {
            // 注意:15.XXX版本才是用如下的(一直给React增加属性导致很臃肿,故而后续版本废弃),16.0以上版本,需要安装prop-types.js
            // name:React.PropTypes.String
            name:PropTypes.string,
            stock:PropTypes.string
        }
        const obj = {name:"猕猴桃",price:13.0,stock:100}
        ReactDOM.render(<XiaoXuComponent {...obj} />,document.getElementById("test"))
    </script>
</body>
</html>

安装prop-types库:

npm i prop-types

在这里插入图片描述
限制必传:

<!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>

    <script src="../node_modules/react/umd/react.development.js"></script>
    <script src = "../node_modules/react-dom/umd/react-dom.development.js"></script>
    <script src="../node_modules/babel-standalone/babel.min.js"></script>
    <!-- PropTypes:限制props -->
    <script src="../node_modules/prop-types/prop-types.js"></script>
</head>
<body>
    <div id="test"></div>
    <div id="test1"></div>
    <script type="text/babel" >
        class XiaoXuComponent extends React.Component{
            state = {isHappy:true,drinks:"芒果饮料"}
            render(){
                console.log(this)
                console.log(this.props)
                const {name,price,stock} = this.props
                return (
                    <ul>
                        <li>水果名称:{name}</li>
                        <li>单价:{price}</li>
                        <li>库存:{stock}</li>
                    </ul>
                )
            }
            cli= ()=>{
            }  
        }

        XiaoXuComponent.propTypes = {
            // 注意:15.XXX版本才是用如下的(一直给React增加属性导致很臃肿,故而后续版本废弃),16.0以上版本,需要安装prop-types.js
            // name:React.PropTypes.String
            name:PropTypes.string,
            stock:PropTypes.string.isRequired
        }
        const obj = {name:"猕猴桃",price:13.13,stock:100.00}
        const obj2 = {name:"青芒",price:99.13}
        ReactDOM.render(<XiaoXuComponent {...obj} />,document.getElementById("test"))
        ReactDOM.render(<XiaoXuComponent {...obj2} />,document.getElementById("test1"))
    </script>
</body>
</html>

提示如下:
在这里插入图片描述
默认值:

<!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>

    <script src="../node_modules/react/umd/react.development.js"></script>
    <script src = "../node_modules/react-dom/umd/react-dom.development.js"></script>
    <script src="../node_modules/babel-standalone/babel.min.js"></script>
    <!-- PropTypes:限制props -->
    <script src="../node_modules/prop-types/prop-types.js"></script>
</head>
<body>
    <div id="test"></div>
    <div id="test1"></div>
    <script type="text/babel" >
        class XiaoXuComponent extends React.Component{
            state = {isHappy:true,drinks:"芒果饮料"}
            render(){
                console.log(this)
                console.log(this.props)
                const {name,price,stock} = this.props
                return (
                    <ul>
                        <li>水果名称:{name}</li>
                        <li>单价:{price}</li>
                        <li>库存:{stock}</li>
                    </ul>
                )
            }
            cli= ()=>{
            }  
        }

        XiaoXuComponent.propTypes = {
            // 注意:15.XXX版本才是用如下的(一直给React增加属性导致很臃肿,故而后续版本废弃),16.0以上版本,需要安装prop-types.js
            // name:React.PropTypes.String
            name:PropTypes.string,
            stock:PropTypes.string.isRequired
        }

        XiaoXuComponent.defaultProps = {
            price:"-",
            stock:"-"
        }
        const obj = {name:"猕猴桃",price:13.13,stock:100.00}
        const obj2 = {name:"青芒"}
        ReactDOM.render(<XiaoXuComponent {...obj} />,document.getElementById("test"))
        ReactDOM.render(<XiaoXuComponent {...obj2} />,document.getElementById("test1"))
    </script>
</body>
</html>

效果如下:
在这里插入图片描述
函数类型的限制:

<!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>

    <script src="../node_modules/react/umd/react.development.js"></script>
    <script src = "../node_modules/react-dom/umd/react-dom.development.js"></script>
    <script src="../node_modules/babel-standalone/babel.min.js"></script>
    <!-- PropTypes:限制props -->
    <script src="../node_modules/prop-types/prop-types.js"></script>
</head>
<body>
    <div id="test"></div>
    <div id="test1"></div>
    <script type="text/babel" >
        class XiaoXuComponent extends React.Component{
            state = {isHappy:true,drinks:"芒果饮料"}
            render(){
                console.log(this)
                console.log(this.props)
                const {name,price,stock} = this.props
                return (
                    <ul>
                        <li>水果名称:{name}</li>
                        <li>单价:{price}</li>
                        <li>库存:{stock}</li>
                    </ul>
                )
            }
            cli= ()=>{
            }  
        }

        XiaoXuComponent.propTypes = {
            // 注意:15.XXX版本才是用如下的(一直给React增加属性导致很臃肿,故而后续版本废弃),16.0以上版本,需要安装prop-types.js
            // name:React.PropTypes.String
            name:PropTypes.string,
            stock:PropTypes.string.isRequired,
            // 限制sale属性必须是function函数,但是function是定义函数的关键字,这里需要用func
            sales:PropTypes.func
        }

        XiaoXuComponent.defaultProps = {
            price:"-",
            stock:"-"
        }
        // const obj = {name:"猕猴桃",price:13.13,stock:100.00,sales:sale}
        const obj = {name:"猕猴桃",price:13.13,stock:100.00,sales:"1"}
        const obj2 = {name:"青芒"}
        ReactDOM.render(<XiaoXuComponent {...obj}/>,document.getElementById("test"))
        ReactDOM.render(<XiaoXuComponent {...obj2} />,document.getElementById("test1"))
        function sale(){
            console.log("我是sale")
        }
    </script>
</body>
</html>

函数限制效果如下:
在这里插入图片描述
2.3.2.3 简写props限制(移动到类中,static关键字定义):

<!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>

    <script src="../node_modules/react/umd/react.development.js"></script>
    <script src = "../node_modules/react-dom/umd/react-dom.development.js"></script>
    <script src="../node_modules/babel-standalone/babel.min.js"></script>
    <!-- PropTypes:限制props -->
    <script src="../node_modules/prop-types/prop-types.js"></script>
</head>
<body>
    <div id="test"></div>
    <div id="test1"></div>
    <script type="text/babel" >
        class XiaoXuComponent extends React.Component{
            static propTypes = {
                // 注意:15.XXX版本才是用如下的(一直给React增加属性导致很臃肿,故而后续版本废弃),16.0以上版本,需要安装prop-types.js
                // name:React.PropTypes.String
                name:PropTypes.string,
                stock:PropTypes.string.isRequired,
                // 限制sale属性必须是function函数,但是function是定义函数的关键字,这里需要用func
                sales:PropTypes.func
            }
            static defaultProps = {
                price:"-",
                stock:"-"
            }
            state = {}
            render(){
                console.log(this)
                console.log(this.props)
                // props是只读的
                // this.props.name = XXX会报错
                const {name,price,stock} = this.props
                return (
                    <ul>
                        <li>水果名称:{name}</li>
                        <li>单价:{price}</li>
                        <li>库存:{stock}</li>
                    </ul>
                )
            }
            cli= ()=>{
            }  
        }
        // XiaoXuComponent.propTypes = {
        //     // 注意:15.XXX版本才是用如下的(一直给React增加属性导致很臃肿,故而后续版本废弃),16.0以上版本,需要安装prop-types.js
        //     // name:React.PropTypes.String
        //     name:PropTypes.string,
        //     stock:PropTypes.string.isRequired,
        //     // 限制sale属性必须是function函数,但是function是定义函数的关键字,这里需要用func
        //     sales:PropTypes.func
        // }
        // XiaoXuComponent.defaultProps = {
        //     price:"-",
        //     stock:"-"
        // }
        const obj = {name:"猕猴桃",price:13.13,stock:100.00,sales:sale}
        const obj2 = {name:"青芒"}
        ReactDOM.render(<XiaoXuComponent {...obj}/>,document.getElementById("test"))
        ReactDOM.render(<XiaoXuComponent {...obj2} />,document.getElementById("test1"))
        function sale(){
            console.log("我是sale")
        }
    </script>
</body>
</html>

2.3.2.4 类式组件中的构造器和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>
    <script src="../node_modules/react/umd/react.development.js"></script>
    <script src = "../node_modules/react-dom/umd/react-dom.development.js"></script>
    <script src="../node_modules/babel-standalone/babel.min.js"></script>
</head>
<body>
    <div id="test"></div>

    <script type="text/babel">
        class Xiaoxu extends React.Component{
            constructor(Xiaoxuprop){
                super(Xiaoxuprop)
                console.log("constructor",Xiaoxuprop)
                // 构造器写了形参,并且调用super(形参),那么就可以在this.props中获取到
                console.log(this.props)
            }
            render(){
                return (
                    <div>
                        <h1>哈哈</h1>
                    </div>
                )
            }
        }
        ReactDOM.render(<Xiaoxu name="Xiaoxu" age= {16}/>,document.getElementById("test"))
    </script>
</body>
</html>

若希望在构造器中通过this.props访问,那么必须按照上述super(props)的写法:
在这里插入图片描述
2.3.2.4 函数式组件使用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>
    <script src="../node_modules/react/umd/react.development.js"></script>
    <script src = "../node_modules/react-dom/umd/react-dom.development.js"></script>
    <script src="../node_modules/babel-standalone/babel.min.js"></script>
</head>
<body>
    <div id="test"></div>

    <script type="text/babel">
        function Xiaoxu(Xiaoxuprop){
            const {name,age,hobby} = Xiaoxuprop
            return (
                <div>
                    <ul>
                        <li>姓名:{name}</li>
                        <li>年龄:{age}</li>
                        <li>兴趣:{hobby}</li>
                    </ul>
                </div>
            )
        }
        ReactDOM.render(<Xiaoxu name="Xiaoxu" age= {16} hobby = "羽毛球"/>,document.getElementById("test"))
    </script>
</body>
</html>

在这里插入图片描述
2.3.3 3/3:refs

2.3.3.1 字符串形式ref:

实际上,对于ref,如果标签涉及到的事件,只需要使用自身的属性时,可以在事件中增加event参数,例如下面的input2,使用event.target.value即可获取值;当然也可同input1,使用this.refs.input1.value来获取数据:

<!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>
    <script src="../node_modules/react/umd/react.development.js"></script>
    <script src = "../node_modules/react-dom/umd/react-dom.development.js"></script>
    <script src="../node_modules/babel-standalone/babel.min.js"></script>
</head>
<body>
    <div id ="test">  
    </div>
    <script type="text/babel">
        class Xiaoxu extends React.Component{
            show = ()=>{
                // const input1 = document.getElementById("test1")
                // console.log(input1.value)
                let {input1} = this.refs
                alert(input1.value)
            }
            show2 = (event)=>{
                alert(event.target.value)
            }
            render(){
                return (
                    <div className="list" ref = "listw">
                        {/*<input id="test1" type= "text" placeholder ="点我提示数据"/>&nbsp; */}
                        <input ref = "input1" type="text" placeholder="点我提示数据"/>&nbsp;
                        <button onClick={this.show}>点我提示左侧数据</button>&nbsp;
                        <input ref="input2" onBlur = {this.show2} type="text" placeholder="失去焦点提示数据"/>
                    </div>
                )
            }
        }
        ReactDOM.render(<Xiaoxu/>,document.getElementById("test"))
    </script>
</body>
</html>

在这里插入图片描述
2.3.3.2 回调形式的ref:

官网参考:https://react.docschina.org/docs/refs-and-the-dom.html

官网上不建议过多使用字符串形式的ref,因为效率较低。推荐使用回调形式和React.createRef()方式。且在React中,不推荐一直使用原生的document.getElementById来获取真实DOM节点(如上例),有封装好的refs提供使用,推荐DOM节点获取使用refs方式。

<!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>
    <script src="../node_modules/react/umd/react.development.js"></script>
    <script src = "../node_modules/react-dom/umd/react-dom.development.js"></script>
    <script src="../node_modules/babel-standalone/babel.min.js"></script>
</head>
<body>
    <div id ="test">  
    </div>
    <script type="text/babel">
        class Xiaoxu extends React.Component{
            show = ()=>{
                // const input1 = document.getElementById("test1")
                // console.log(input1.value)
                // let {input1} = this.refs
                // alert(input1.value)
                // 回调形式的ref就是把当前input节点标签node,赋值给了类的实例属性:this.input1,
                // 故而可以直接使用this.input1.value
                alert(this.input1.value)
            }
            // show2 = (event)=>{
            //     alert(event.target.value)
            // }
            show2 = ()=>{
                const {input2} = this
                alert(input2.value)
            }
            render(){
                return (
                    <div className="list" ref = "listw">
                        <input ref = {node=>this.input1=node} type="text" placeholder="点我提示数据"/>&nbsp;
                        <button onClick={this.show}>点我提示左侧数据</button>&nbsp;
                        <input ref={node=>this.input2=node} onBlur = {this.show2} type="text" placeholder="失去焦点提示数据"/>
                    </div>
                )
            }
        }
        ReactDOM.render(<Xiaoxu/>,document.getElementById("test"))
    </script>
</body>
</html>

在这里插入图片描述
2.3.3.3 内联函数回调形式的ref:

如果写成下述内联函数形式的ref,在组件更新时,第一次node节点是null,第二次才是真实的node节点(官网中有提到)。组件更新次数为1+n次,第1次是render直接更新,n是由state更新引起的(state理论可以更新n次),只要React组件3种属性之一的state更新了,就会触发render。

<!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>
    <script src="../node_modules/react/umd/react.development.js"></script>
    <script src = "../node_modules/react-dom/umd/react-dom.development.js"></script>
    <script src="../node_modules/babel-standalone/babel.min.js"></script>
</head>
<body>
    <div id ="test">  
    </div>
    <script type="text/babel">
        class Xiaoxu extends React.Component{
            show = ()=>{

            }
            change1 = () =>{
                const {isHappy} = this.state
                this.setState({isHappy:!isHappy})
            }
            // 下面ref是内联函数形式
            state = {isHappy:true}
            render(){
                const {isHappy} = this.state

                return (
                    <div className="list" ref = "listw">
                        <h1>我很{isHappy?"开心":"悲伤"}</h1>
                        <input ref = {node=>{this.input1=node;console.log("我来了",node)}} type="text"/>
                        <button onClick={this.show}>点我提示输入数据</button>&nbsp;
                        <button onClick={this.change1}>点我切换心情</button>&nbsp;
                    </div>
                )
            }
        }
        ReactDOM.render(<Xiaoxu/>,document.getElementById("test"))
    </script>
</body>
</html>

在这里插入图片描述
因为每次渲染都会创建一个新的函数实例,React会清空旧的ref并设置新的。官网提供的解决方式:将ref的回调函数定义为class的绑定函数即可避免上述问题。
如下可解决:

<!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>
    <script src="../node_modules/react/umd/react.development.js"></script>
    <script src = "../node_modules/react-dom/umd/react-dom.development.js"></script>
    <script src="../node_modules/babel-standalone/babel.min.js"></script>
</head>
<body>
    <div id ="test">  
    </div>
    <script type="text/babel">
        class Xiaoxu extends React.Component{
            show = ()=>{

            }
            change1 = () =>{
                const {isHappy} = this.state
                this.setState({isHappy:!isHappy})
            }
            haha = (node)=>{
                this.input1=node;
                console.log("我来了",node);
            }
            // 下面ref是内联函数形式
            state = {isHappy:true}
            render(){
                const {isHappy} = this.state

                return (
                    <div className="list" ref = "listw">
                        <h1>我很{isHappy?"开心":"悲伤"}</h1>
                        <input ref = {this.haha} type="text"/>
                        <button onClick={this.show}>点我提示输入数据</button>&nbsp;
                        <button onClick={this.change1}>点我切换心情</button>&nbsp;
                    </div>
                )
            }
        }
        ReactDOM.render(<Xiaoxu/>,document.getElementById("test"))
    </script>
</body>
</html>

在这里插入图片描述
2.3.3.4 createRef形式创建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>
    <script src="../node_modules/react/umd/react.development.js"></script>
    <script src = "../node_modules/react-dom/umd/react-dom.development.js"></script>
    <script src="../node_modules/babel-standalone/babel.min.js"></script>
</head>
<body>
    <div id ="test">  
    </div>
    <script type="text/babel">
        class Xiaoxu extends React.Component{
            // React.createRef调用后返回一个容器,该容器可以存储被ref所标识的节点
            myRef = React.createRef()
            show = ()=>{
                console.log(this.myRef)
            }
            // 下面ref是创建的
            render(){

                return (
                    <div className="list" ref = "listw">
                        <input ref = {this.myRef} type="text" placeholder = "提示数据"/>
                        <button onClick={this.show}>点我提示输入数据</button>&nbsp;
                    </div>
                )
            }
        }
        ReactDOM.render(<Xiaoxu/>,document.getElementById("test"))
    </script>
</body>
</html>

在这里插入图片描述

<!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>
    <script src="../node_modules/react/umd/react.development.js"></script>
    <script src = "../node_modules/react-dom/umd/react-dom.development.js"></script>
    <script src="../node_modules/babel-standalone/babel.min.js"></script>
</head>
<body>
    <div id ="test">  
    </div>
    <script type="text/babel">
        class Xiaoxu extends React.Component{
            // React.createRef调用后返回一个容器,该容器可以存储被ref所标识的节点
            // 容器里面只能存储一个
            myRef = React.createRef()
            myRef2 = React.createRef()
            show = ()=>{
                console.log(this.myRef.current.value)
                console.log(this.myRef2.current.value)
            }
            // 下面ref是创建的
            render(){

                return (
                    <div className="list" ref = "listw">
                        <input ref = {this.myRef} type="text" placeholder = "提示数据"/>
                        <button onClick={this.show}>点我提示输入数据</button>&nbsp;
                        <input ref = {this.myRef2} type="text" placeholder = "提示数据"/>
                    </div>
                )
            }
        }
        ReactDOM.render(<Xiaoxu/>,document.getElementById("test"))
    </script>
</body>
</html>

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值