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 ="点我提示数据"/> */}
<input ref = "input1" type="text" placeholder="点我提示数据"/>
<button onClick={this.show}>点我提示左侧数据</button>
<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="点我提示数据"/>
<button onClick={this.show}>点我提示左侧数据</button>
<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>
<button onClick={this.change1}>点我切换心情</button>
</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>
<button onClick={this.change1}>点我切换心情</button>
</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>
</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>
<input ref = {this.myRef2} type="text" placeholder = "提示数据"/>
</div>
)
}
}
ReactDOM.render(<Xiaoxu/>,document.getElementById("test"))
</script>
</body>
</html>