React面向组件编程
- 基本理解和使用
- 组件三大核心属性1: state
- 效果
- 理解
- 强烈注意
- 组件三大核心属性2: props
- 效果
- 理解
- 作用
- 编码操作
- 组件三大核心属性3: refs与事件处理
- 效果
- 理解
- 编码
- 2.4.4. 事件处理
- 收集表单数据
- 效果
- 理解
- 组件的生命周期(重要)
- 效果
- 理解
- 生命周期流程图(旧)
- 生命周期流程图(新)
- 重要的勾子
- 即将废弃的勾子
基本理解和使用
使用React开发者工具调试
效果
函数式组件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>使用jsx创建虚拟DOM</title>
</head>
<body>
<!--准备好一个容器-->
<div id="test">
</div>
<!--react的核心库-->
<script type="text/javascript" src="../js/react.development.js"></script>
<!--react的扩展库,专门用于操作DOM-->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!--引入babel,用于解析jsx语法为原生js语法-->
<script type="text/javascript" src="../js/babel.min.js"></script>
<!-- babel将jsx代码翻译为js代码 -->
<script type="text/babel">
// 1. 创建函数式组件,组件首字母大写
function MyComponent(){
console.log(this) // 此处的this时undefined,因为Babel编译后开启了严格模式
return <h2>我是用函数定义的组件(适用于【简单组件】的定义)</h2>
}
// 2.渲染组件到页面
ReactDOM.render(<MyComponent/>, document.getElementById('test'))
/**
* 执行了ReactDOM.render(<MyComponent/>, document.getElementById('test')))之后发生了什么
* 1.react会解析组件标签,找到了MyComponent组件
* 2.发现组件是使用函数定义的,随后调用该函数,蒋返回的虚拟DOM转为真实DOM,随后呈现在页面中
* **/
</script>
</body>
</html>
类式组件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>使用jsx创建虚拟DOM</title>
</head>
<body>
<!--准备好一个容器-->
<div id="test">
</div>
<!--react的核心库-->
<script type="text/javascript" src="../js/react.development.js"></script>
<!--react的扩展库,专门用于操作DOM-->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!--引入babel,用于解析jsx语法为原生js语法-->
<script type="text/javascript" src="../js/babel.min.js"></script>
<!-- babel将jsx代码翻译为js代码 -->
<script type="text/babel">
// 1. 创建类式组件,必须要继承React.Component
class MyComponent extends React.Component{
render(){
// render方法放在了那里?- MyComponent类的原型对象上,供实例对象使用
// render中的this实施诶---MyComponent组件实例对象
console.log("render中的this:", this)
return <h2>我是用类定义的组件(适用于【复杂组件】的定义)</h2>
}
}
// 2.渲染组件到页面
ReactDOM.render(<MyComponent/>, document.getElementById('test'))
/**
* 执行了ReactDOM.render(<MyComponent/>, document.getElementById('test'))之后发生了什么
* 1.react会解析组件标签,找到了MyComponent组件
* 2.发现组件是使用类定义的,随后new出来该类的实例,并通过该实例调用到原型上的render方法
* 3.将render返回的虚拟DOM转为真实DOM,随后呈现在页面中
* **/
/**
* 组件的三大属性:
props:{}
refs:{}
state:null
* **/
</script>
</body>
</html>
复杂组件的状态state如何理解:
组件中的状态驱动页面渲染
组件实例对象生成的state
注意
1.组件名必须首字母大写
2.虚拟DOM元素只能有一个根元素
3.虚拟DOM元素必须有结束标签
渲染类组件标签的基本流程
1.React内部会创建组件实例对象
2.调用render()得到虚拟DOM, 并解析为真实DOM
3.插入到指定的页面元素内部
组件三大核心属性1: state
效果
需求: 定义一个展示天气信息的组件
1.默认展示天气炎热 或 凉爽
2.点击文字切换天气
state.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>使用jsx创建虚拟DOM</title>
</head>
<body>
<!--准备好一个容器-->
<div id="test">
</div>
<!--react的核心库-->
<script type="text/javascript" src="../js/react.development.js"></script>
<!--react的扩展库,专门用于操作DOM-->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!--引入babel,用于解析jsx语法为原生js语法-->
<script type="text/javascript" src="../js/babel.min.js"></script>
<!-- babel将jsx代码翻译为js代码 -->
<script type="text/babel">
// let that
// 1.创建组件
class Weather extends React.Component{
// 参数取决于new的时候需要传递的内容
// 构造器调用几次?----- 1次
constructor(props){
console.log('constructor')
super(props)
// 初始化状态
this.state = {isHot:false, wind:'微风'}
// that = this
// 解决changeWeather中this指向问题,将原型上的changeWeather挂在到实例对象上
this.changeWeather = this.changeWeather.bind(this)
}
// render调用几次-----1+n次,1是初始化次数,n是状态更新的次数
render(){
console.log('render')
// 读取状态
const {isHot, wind} = this.state
// react中onClick要大写,函数后面不需要加括号,只用指定函数,由react调用
return <h1 onClick = {this.changeWeather}> 今天天气很{isHot ? '炎热':'凉爽'} , {wind}</h1>
}
// changeWeaher调用几次?点几次调几次
changeWeather(){
// changeWeather放在那里-----类的原型对象上,供实例使用
// 通过Weather实例对象调用changeWeather时,changeWeather中的this就是Weather实例
// 为什么这里this.state是undefined,因为他不是通过实例调用
// 由于changeWeather是作为onClick回调,所以不是通过实例嗲偶用的,是直接调用
// 类中的方法默认开启了局部的严格模式,所以changeWeather中的this为undefined
// console.log(this.state.isHot)
// 获取原来的isHot值
const isHot = this.state.isHot
// 严重注意:状态state不能直接更改,下面这行就是直接更改,要借助一个内置API
// this.state.isHot = !isHot // 这是错误写法
// 严重注意,状态必须通过setState更改,且更新是一种合并的操作
this.setState({isHot:!isHot})
}
}
// 2.渲染组件到页面
ReactDOM.render(<Weather/>, document.getElementById('test'))
// const title = document.getElementById('title')
// title.addEventListener('click', ()=>{
// console.log("标题被点击了")
// })
// const title = document.getElementById('title')
// title.onclick = ()=>{
// console.log("标题被点击了")
// }
// function changeWeather(){
// alert('此处修改isHot的值')
// // 这里的this是window
// // const {isHot} = this.state
// // console.log(isHot)
// console.log(that.state.isHot)
// }
</script>
</body>
</html>
间写
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>使用jsx创建虚拟DOM</title>
</head>
<body>
<!--准备好一个容器-->
<div id="test">
</div>
<!--react的核心库-->
<script type="text/javascript" src="../js/react.development.js"></script>
<!--react的扩展库,专门用于操作DOM-->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!--引入babel,用于解析jsx语法为原生js语法-->
<script type="text/javascript" src="../js/babel.min.js"></script>
<!-- babel将jsx代码翻译为js代码 -->
<script type="text/babel">
class Weather extends React.Component{
// 初始化状态
state = {isHot:false, wind:'微风'}
render(){
const {isHot, wind} = this.state
return <h1 onClick = {this.changeWeather}> 今天天气很{isHot ? '炎热':'凉爽'} , {wind}</h1>
}
// 自定义方法-----要用赋值语句的形式+箭头函数
// 将function改成了箭头函数,可以实现,箭头函数没有自己的this,
// 在里面写this不会报错,它会找外层函数的this,作为箭头函数的this去使用
changeWeather = ()=>{
const isHot = this.state.isHot
this.setState({isHot:!isHot})
}
}
// 2.渲染组件到页面
ReactDOM.render(<Weather/>, document.getElementById('test'))
</script>
</body>
</html>
理解
1.state是组件对象最重要的属性, 值是对象(可以包含多个key-value的组合)
2.组件被称为"状态机", 通过更新组件的state来更新对应的页面显示(重新渲染组件)
强烈注意
1.组件中render方法中的this为组件实例对象
2.组件自定义的方法中this为undefined,如何解决?
a)强制绑定this: 通过函数对象的bind()
b)箭头函数
3.状态数据,不能直接修改或更新
组件三大核心属性2: props
效果
需求: 自定义用来显示一个人员信息的组件
1.姓名必须指定,且为字符串类型;
2.性别为字符串类型,如果性别没有指定,默认为男
3.年龄为字符串类型,且为数字类型,默认值为18
理解
1.每个组件对象都会有props(properties的简写)属性
2.组件标签的所有属性都保存在props中
作用
1.通过标签属性从组件外向组件内传递变化的数据
2.注意: 组件内部不要修改props数据
编码操作
1.内部读取某个属性值
this.props.name
2.对props中的属性值进行类型限制和必要性限制
第一种方式(React v15.5 开始已弃用):
Person.propTypes = {
name: React.PropTypes.string.isRequired,
age: React.PropTypes.number
}
第二种方式(新):使用prop-types库进限制(需要引入prop-types库)
Person.propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number.
}
3.扩展属性: 将对象的所有属性通过props传递
<Person {...person}/>
4.默认属性值:
Person.defaultProps = {
age: 18,
sex:'男'
}
5.组件类的构造函数
constructor(props){
super(props)
console.log(props)//打印所有属性
}
props基本使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>使用jsx创建虚拟DOM</title>
</head>
<body>
<!--准备好一个容器-->
<div id="test1">
</div>
<div id="test2">
</div>
<div id="test3">
</div>
<!--react的核心库-->
<script type="text/javascript" src="../js/react.development.js"></script>
<!--react的扩展库,专门用于操作DOM-->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!--引入babel,用于解析jsx语法为原生js语法-->
<script type="text/javascript" src="../js/babel.min.js"></script>
<!-- babel将jsx代码翻译为js代码 -->
<script type="text/babel">
// 1.创建组件
class Person extends React.Component{
render(){
console.log(this.props)
const {name, sex, age} = this.props
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age+1}</li>
</ul>
)
}
}
// 类似html标签key-value的形式,,注意要传递number类型的参数,不能直接使用引号,需要用大括号括起来
// 对传递的标签属性age进行类型鉴定,name进行必要性鉴定,性别需要指定默认值
ReactDOM.render(<Person name="tom" age={19} sex='女'/>, document.getElementById('test1'))
ReactDOM.render(<Person name="java" age={20} sex='男'/>, document.getElementById('test2'))
// 要保证两者属性名全部相同
//仅适用于标签属性的传递
const p = {name:'老刘', age:18, sex:'女'}
ReactDOM.render(<Person {...p}/>, document.getElementById('test3'))
// 传递props,就是传递标签属性
</script>
</body>
</html>
props进行限制
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>使用jsx创建虚拟DOM</title>
</head>
<body>
<!--准备好一个容器-->
<div id="test1">
</div>
<div id="test2">
</div>
<div id="test3">
</div>
<!--react的核心库,React对象-->
<script type="text/javascript" src="../js/react.development.js"></script>
<!--react的扩展库,专门用于操作DOM, ReactDom对象-->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!--引入babel,用于解析jsx语法为原生js语法-->
<script type="text/javascript" src="../js/babel.min.js"></script>
<!---引入prop-types,用于对组件标签属性进行限制,PropType对象-->
<script type="text/javascript" src="../js/prop-types.js"></script>
<!-- babel将jsx代码翻译为js代码 -->
<script type="text/babel">
// 1.创建组件
class Person extends React.Component{
render(){
console.log(this.props)
const {name, sex, age} = this.props
// props是只读的
//this.props.name='java' // 此行代码会报错,因为props是只读的
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age+1}</li>
</ul>
)
}
}
//对标签属性进行类型、必要性的限制
Person.propTypes = {
name:PropTypes.string.isRequired, //限制name必传,且为字符串
sex:PropTypes.string, //限制sex字符串
age:PropTypes.number, //限制age为数值
speak:PropTypes.func //限制speak为函数
}
// 指定默认标签属性值
Person.defaultProps = {
sex:'男' , // sex默认值为男
age:18 //age默认值为18
}
ReactDOM.render(<Person name="tom" age={19} sex='女'/>, document.getElementById('test1'))
ReactDOM.render(<Person name="java" speak={speak}/>, document.getElementById('test2'))
const p = {name:'老刘', age:18, sex:'女'}
ReactDOM.render(<Person {...p}/>, document.getElementById('test3'))
function speak(){
console.log('我说话了')
}
</script>
</body>
</html>
props简写方式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>使用jsx创建虚拟DOM</title>
</head>
<body>
<!--准备好一个容器-->
<div id="test1">
</div>
<div id="test2">
</div>
<div id="test3">
</div>
<!--react的核心库,React对象-->
<script type="text/javascript" src="../js/react.development.js"></script>
<!--react的扩展库,专门用于操作DOM, ReactDom对象-->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!--引入babel,用于解析jsx语法为原生js语法-->
<script type="text/javascript" src="../js/babel.min.js"></script>
<!---引入prop-types,用于对组件标签属性进行限制,PropType对象-->
<script type="text/javascript" src="../js/prop-types.js"></script>
<!-- babel将jsx代码翻译为js代码 -->
<script type="text/babel">
// 1.创建组件
class Person extends React.Component {
//对标签属性进行类型、必要性的限制
static propTypes = {
name: PropTypes.string.isRequired, //限制name必传,且为字符串
sex: PropTypes.string, //限制sex字符串
age: PropTypes.number, //限制age为数值
speak: PropTypes.func //限制speak为函数
}
// 指定默认标签属性值
static defaultProps = {
sex: '男', // sex默认值为男
age: 18 //age默认值为18
}
render() {
console.log(this.props)
const { name, sex, age } = this.props
// props是只读的
//this.props.name = 'java' // 此行代码会报错,因为props是只读的
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age + 1}</li>
</ul>
)
}
}
ReactDOM.render(<Person name="tom" age={19} sex='女' />, document.getElementById('test1'))
ReactDOM.render(<Person name="java" speak={speak} />, document.getElementById('test2'))
const p = { name: '老刘', age: 18, sex: '女' }
ReactDOM.render(<Person {...p} />, document.getElementById('test3'))
function speak() {
console.log('我说话了')
}
</script>
</body>
</html>
props与构造器
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>使用jsx创建虚拟DOM</title>
</head>
<body>
<!--准备好一个容器-->
<div id="test1">
</div>
<div id="test2">
</div>
<div id="test3">
</div>
<!--react的核心库,React对象-->
<script type="text/javascript" src="../js/react.development.js"></script>
<!--react的扩展库,专门用于操作DOM, ReactDom对象-->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!--引入babel,用于解析jsx语法为原生js语法-->
<script type="text/javascript" src="../js/babel.min.js"></script>
<!---引入prop-types,用于对组件标签属性进行限制,PropType对象-->
<script type="text/javascript" src="../js/prop-types.js"></script>
<!-- babel将jsx代码翻译为js代码 -->
<script type="text/babel">
// 1.创建组件
class Person extends React.Component {
// 类中的构造器有什么作用呢?有必要传props吗?
// 在react中,构造函数仅用于以下两种情况
// 1.通过给this.state赋值对象来初始化内部state,2.为事件处理函数绑定实例
// 类中的构造器可有可无
// 在react组件挂在致癌案,会调用它的构造函数,在为react.component子类实现构造函数时,应在
// 其他语句之前调用super(props),否则this.props在构造函数中可能会出现为定义异常
constructor(props) {
// 构造器是否接收props,是否传递给super,取决于:是否希望在构造器中通过this访问props
console.log(props)
super(props)
}
//对标签属性进行类型、必要性的限制
static propTypes = {
name: PropTypes.string.isRequired, //限制name必传,且为字符串
sex: PropTypes.string, //限制sex字符串
age: PropTypes.number, //限制age为数值
speak: PropTypes.func //限制speak为函数
}
// 指定默认标签属性值
static defaultProps = {
sex: '男', // sex默认值为男
age: 18 //age默认值为18
}
render() {
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age + 1}</li>
</ul>
)
}
}
ReactDOM.render(<Person name="tom"/>, document.getElementById('test1'))
</script>
</body>
</html>
函数式组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>使用jsx创建虚拟DOM</title>
</head>
<body>
<!--准备好一个容器-->
<div id="test1">
</div>
<div id="test2">
</div>
<div id="test3">
</div>
<!--react的核心库,React对象-->
<script type="text/javascript" src="../js/react.development.js"></script>
<!--react的扩展库,专门用于操作DOM, ReactDom对象-->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!--引入babel,用于解析jsx语法为原生js语法-->
<script type="text/javascript" src="../js/babel.min.js"></script>
<!---引入prop-types,用于对组件标签属性进行限制,PropType对象-->
<script type="text/javascript" src="../js/prop-types.js"></script>
<!-- babel将jsx代码翻译为js代码 -->
<script type="text/babel">
// 1.函数式组件只能使用props
function Person(props) {
const { name, age, sex } = props
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age + 1}</li>
</ul>
)
}
//对标签属性进行类型、必要性的限制
Person.propTypes = {
name: PropTypes.string.isRequired, //限制name必传,且为字符串
sex: PropTypes.string, //限制sex字符串
age: PropTypes.number, //限制age为数值
speak: PropTypes.func //限制speak为函数
}
// 指定默认标签属性值
Person.defaultProps = {
sex: '男', // sex默认值为男
age: 18 //age默认值为18
}
ReactDOM.render(<Person name="tom" />, document.getElementById('test1'))
</script>
</body>
</html>
组件三大核心属性3: refs与事件处理
效果
需求: 自定义组件, 功能说明如下:
点击按钮, 提示第一个输入框中的值
当第2个输入框失去焦点时, 提示这个输入框中的值
理解
组件内的标签可以定义ref属性来标识自己(类似id)
编码
1.字符串形式的ref
<input ref="input1"/>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>使用jsx创建虚拟DOM</title>
</head>
<body>
<!--准备好一个容器-->
<div id="test">
</div>
<!--react的核心库-->
<script type="text/javascript" src="../js/react.development.js"></script>
<!--react的扩展库,专门用于操作DOM-->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!--引入babel,用于解析jsx语法为原生js语法-->
<script type="text/javascript" src="../js/babel.min.js"></script>
<!-- babel将jsx代码翻译为js代码 -->
<script type="text/babel">
// 1. 创建类式组件,必须要继承React.Component
class Demo extends React.Component{
// 展示左侧输入框的数据
showData = ()=>{
// 注意收集出来的属性是refs
const {input1} = this.refs
console(input1)
alert(input1.value)
}
// 展示右侧输入框的数据
showData2 = ()=>{
const {input2} = this.refs
alert(input2.value)
}
render(){
return (
// 打了标识之后,都收集到组件对象的refs属性中
<div>
<input ref = "input1" type="text" placeholder="点击按钮提示数据"/>
<button ref = "button1" onClick={this.showData}>点我提示左侧的数据</button>
<input onBlur = {this.showData2} ref = "input2" type="text" placeholder="失去焦点提示数据"/>
</div>
)
}
}
// 2.渲染组件到页面
ReactDOM.render(<Demo/>, document.getElementById('test'))
</script>
</body>
</html>
2.回调形式的ref
<input ref={(c)=>{this.input1 = c}}/>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>使用jsx创建虚拟DOM</title>
</head>
<body>
<!--准备好一个容器-->
<div id="test">
</div>
<!--react的核心库-->
<script type="text/javascript" src="../js/react.development.js"></script>
<!--react的扩展库,专门用于操作DOM-->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!--引入babel,用于解析jsx语法为原生js语法-->
<script type="text/javascript" src="../js/babel.min.js"></script>
<!-- babel将jsx代码翻译为js代码 -->
<script type="text/babel">
// 1. 创建类式组件,必须要继承React.Component
class Demo extends React.Component{
// 展示左侧输入框的数据
showData = ()=>{
const{input1} = this
alert(input1.value)
}
// 展示右侧输入框的数据
showData2 = ()=>{
const {input2} = this
alert(input2.value)
}
render(){
return (
// 打了标识之后,都收集到组件对象的refs属性中
/**
* <input ref = {(a)=>{this.input1 = a}} type="text" placeholder="点击按钮提示数据"/>
* 这句的含义是把ref当前所处的节点挂在了当前实例自身上,并且取了个名字叫input1
* react会自动帮你调用回调函数
* **/
<div>
<input ref = {c => this.input1 = c } type="text" placeholder="点击按钮提示数据"/>
<button ref = "" onClick={this.showData}>点我提示左侧的数据</button>
<input onBlur = {this.showData2} ref = {c => this.input2 = c } type="text" placeholder="失去焦点提示数据"/>
</div>
)
}
}
// 2.渲染组件到页面
ReactDOM.render(<Demo/>, document.getElementById('test'))
</script>
</body>
</html>
回调中的函数执行次数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>使用jsx创建虚拟DOM</title>
</head>
<body>
<!--准备好一个容器-->
<div id="test">
</div>
<!--react的核心库-->
<script type="text/javascript" src="../js/react.development.js"></script>
<!--react的扩展库,专门用于操作DOM-->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!--引入babel,用于解析jsx语法为原生js语法-->
<script type="text/javascript" src="../js/babel.min.js"></script>
<!-- babel将jsx代码翻译为js代码 -->
<script type="text/babel">
// 1. 创建类式组件,必须要继承React.Component
class Demo extends React.Component{
state = {isHot:true}
showInfo = ()=>{
const {input1} = this
alert(input1.value)
}
changeWeather = ()=>{
const {isHot} = this.state
// 获取原来的天气
this.setState({isHot:!isHot})
}
saveInput = (c)=>{
this.input1 = c;
console.log('@',c)
}
render(){
const {isHot} = this.state
return (
/**
* 如果 ref 回调函数是以内联函数的方式定义的,在更新过程中它会被执行两次,第一次传入参数 null,然后第二次会传入参数 DOM 元素。
* 这是因为在每次渲染时会创建一个新的函数实例,所以 React 清空旧的 ref 并且设置新的。
* 通过将 ref 的回调函数定义成 class 的绑定函数的方式可以避免上述问题,但是大多数情况下它是无关紧要的。
* **/
<div>
<h2>今天天气很{isHot ? '炎热':'寒冷'} </h2>
{/*<input ref = {(c) => {this.input1 = c;console.log('@', c);}} type="text" />*/}
<input ref = {this.saveInput} type="text" />
<button onClick={this.showInfo}>点我提示左侧的数据</button>
<button onClick={this.changeWeather}>点我切换天气</button>
</div>
)
}
}
// 2.渲染组件到页面
ReactDOM.render(<Demo/>, document.getElementById('test'))
</script>
</body>
</html>
3.createRef创建ref容器·
myRef = React.createRef()
<input ref={this.myRef}/>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>使用jsx创建虚拟DOM</title>
</head>
<body>
<!--准备好一个容器-->
<div id="test">
</div>
<!--react的核心库-->
<script type="text/javascript" src="../js/react.development.js"></script>
<!--react的扩展库,专门用于操作DOM-->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!--引入babel,用于解析jsx语法为原生js语法-->
<script type="text/javascript" src="../js/babel.min.js"></script>
<!-- babel将jsx代码翻译为js代码 -->
<script type="text/babel">
// 1. 创建类式组件,必须要继承React.Component
class Demo extends React.Component{
/**
* 它本身是一个函数,React.createRef()调用后可以返回一个容器,调用后可以存储被ref所标识的节点,
* 改容器是专人专用的
* **/
myRef = React.createRef()
myRef2 = React.createRef()
// 展示左侧输入框的数据
showData = ()=>{
alert(this.myRef.current.value)
}
// 展示右侧输入框的数据
showData2 = ()=>{
alert(this.myRef2.current.value)
}
render(){
return (
<div>
<input ref = {this.myRef} type="text" placeholder="点击按钮提示数据"/>
<button ref = "" onClick={this.showData}>点我提示左侧的数据</button>
<input onBlur={this.showData2} ref = {this.myRef2} type="text" placeholder="失去焦点提示数据"/>
</div>
)
}
}
// 2.渲染组件到页面
ReactDOM.render(<Demo/>, document.getElementById('test'))
</script>
</body>
</html>
2.4.4. 事件处理
1.通过onXxx属性指定事件处理函数(注意大小写)
- 1)React使用的是自定义(合成)事件, 而不是使用的原生DOM事件
- 2)React中的事件是通过事件委托方式处理的(委托给组件最外层的元素)
2.通过event.target得到发生事件的DOM元素对象
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>使用jsx创建虚拟DOM</title>
</head>
<body>
<!--准备好一个容器-->
<div id="test">
</div>
<!--react的核心库-->
<script type="text/javascript" src="../js/react.development.js"></script>
<!--react的扩展库,专门用于操作DOM-->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!--引入babel,用于解析jsx语法为原生js语法-->
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/babel">
// 1. 创建类式组件
class Demo extends React.Component {
/**
* 1.通过onXxx属性指定事件处理函数(注意大小写,react是大写,原生是小写),
1)React使用的是自定义(合成)事件, 而不是使用的原生DOM事件 为了更好的兼容性
2)React中的事件是通过事件委托方式处理的(委托给组件最外层的元素) 为了更高效
2.通过event.target得到发生事件的DOM元素对象 ----不要过度使用ref
* **/
// 创建ref容器
myRef = React.createRef()
myRef2 = React.createRef()
// 展示左侧输入框的数据
showData = () => {
alert(this.myRef.current.value)
}
// 展示右侧输入框的数据
showData2 = () => {
alert(event.target.value)
}
render() {
return (
<div>
<input ref={this.myRef} type="text" placeholder="点击按钮提示数据" />
<button ref="" onClick={this.showData}>点我提示左侧的数据</button>
{/**发生事件的元素是正要操作的元素,ref可以省略**/}
<input onBlur={this.showData2} type="text" placeholder="失去焦点提示数据" />
</div>
)
}
}
// 2.渲染组件到页面
ReactDOM.render(<Demo />, document.getElementById('test'))
</script>
</body>
</html>
收集表单数据
效果
需求: 定义一个包含表单的组件 输入用户名密码后, 点击登录提示输入信息
理解
包含表单的组件分类
1.受控组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>使用jsx创建虚拟DOM</title>
</head>
<body>
<!--准备好一个容器-->
<div id="test">
</div>
<!--react的核心库-->
<script type="text/javascript" src="../js/react.development.js"></script>
<!--react的扩展库,专门用于操作DOM-->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!--引入babel,用于解析jsx语法为原生js语法-->
<script type="text/javascript" src="../js/babel.min.js"></script>
<!-- babel将jsx代码翻译为js代码 -->
<script type="text/babel">
// 随着输入维护状态,就称之为非受控组件
// 创建组件
class Login extends React.Component{
state = {
username:'', //用户名
password:'' //密码
}
handleSubmit = (event)=>{
event.preventDefault() // 阻止表单提交,不会跳转到新的页面
const {username, password} = this.state
alert(`输入的用户名是:${username}, 你输入的密码是:${password}`)
}
// 保存用户名到状态中
saveUsername = (event)=>{
this.setState({username: event.target.value})
}
// 保存用户名到密码中
savePassword = (event)=>{
this.setState({password: event.target.value})
}
render() {
return (
// 不指定的话,默认发get请求,带的参数默认是get参数
<form action="http://www.baidu.com" onSubmit={this.handleSubmit}>
用户名:<input onChange={this.saveUsername} type = "text" name ="username"/>
密码:<input onChange={this.savePassword} type = "password" name="password"/>
<button>登陆</button>
</form>
)
}
}
ReactDOM.render(<Login/>, document.getElementById('test'))
</script>
</body>
</html>
2.非受控组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>使用jsx创建虚拟DOM</title>
</head>
<body>
<!--准备好一个容器-->
<div id="test">
</div>
<!--react的核心库-->
<script type="text/javascript" src="../js/react.development.js"></script>
<!--react的扩展库,专门用于操作DOM-->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!--引入babel,用于解析jsx语法为原生js语法-->
<script type="text/javascript" src="../js/babel.min.js"></script>
<!-- babel将jsx代码翻译为js代码 -->
<script type="text/babel">
// 创建组件
class Login extends React.Component{
// 页面所有输入类的dom,都是现用现取,就是非受控组件
//
handleSubmit = (event)=>{
event.preventDefault() // 阻止表单提交,不会跳转到新的页面
const {username, password} = this
alert(`输入的用户名是:${username.value}, 你输入的密码是:${password.value}`)
}
render() {
return (
// 不指定的话,默认发get请求,带的参数默认是get参数
<form action="http://www.baidu.com" onSubmit={this.handleSubmit}>
用户名:<input ref={c=>this.username=c} type = "text" name ="username"/>
密码:<input ref={c=>this.password=c} type = "password" name="password"/>
<button>登陆</button>
</form>
)
}
}
ReactDOM.render(<Login/>, document.getElementById('test'))
</script>
</body>
</html>
组件的生命周期(重要)
效果
需求:定义组件实现以下功能:
让指定的文本做显示 / 隐藏的渐变动画
从完全可见,到彻底消失,耗时2S
点击“不活了”按钮从界面中卸载组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>使用jsx创建虚拟DOM</title>
</head>
<body>
<!--准备好一个容器-->
<div id="test">
</div>
<!--react的核心库-->
<script type="text/javascript" src="../js/react.development.js"></script>
<!--react的扩展库,专门用于操作DOM-->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!--引入babel,用于解析jsx语法为原生js语法-->
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/babel">
// 创建组件
// 生命周期回调函数<===>生命周期钩子函数
// react会在合适的时机把函数勾起来执行。
class Life extends React.Component {
// 状态的数据驱动页面的更新
state = { opacity: 1 }
death = () => {
// 卸载组件unmount
ReactDOM.unmountComponentAtNode(document.getElementById('test'))
}
// action =()=>{
// setInterval(()=>{
// // 获取原来状态
// let {opacity} = this.state
// // 减小0.1
// opacity -= 1
// if (opacity <= 0) opacity = 1
// // 设置新的透明度
// this.setState({opacity})
// }, 200);
// }
// 名字一定不要写错
// 组件挂载完毕之后调用
componentDidMount() {
this.timer = setInterval(() => {
// 获取原来状态
let { opacity } = this.state
// 减小0.1
opacity -= 0.1
if (opacity <= 0) opacity = 1
// 设置新的透明度
this.setState({opacity})
}, 200);
}
//组件将要卸载
componentWillUnmount(){
// 清空定时器
clearInterval(this.timer)
}
// render调用1+n次,调用的时机:初始化渲染,状态更新之后
render() {
// 无线递归,定时器每隔200s都会更新状态,更新状态就会导致组件重新挂在,render再次执行,定时器又重新初始化了
//
console.log('render')
// setInterval(()=>{
// // 获取原来状态
// let {opacity} = this.state
// // 减小0.1
// opacity -= 1
// if (opacity <= 0) opacity = 1
// // 设置新的透明度
// this.setState({opacity})
// }, 200);
return (
<div>
<h2 style={{ opacity: this.state.opacity }}>React学不会怎么办</h2>
<button onClick={this.death}>不活了</button>
<button onClick={this.action}>开始变化</button>
</div>
)
}
}
// 渲染组件,实际上就是挂载(mount),
ReactDOM.render(<Life />, document.getElementById('test'))
</script>
</body>
</html>
理解
1.组件从创建到死亡它会经历一些特定的阶段。
2.React组件中包含一系列勾子函数(生命周期回调函数), 会在特定的时刻调用。
3.我们在定义组件时,会在特定的生命周期回调函数中,做特定的工作。
生命周期流程图(旧)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>使用jsx创建虚拟DOM</title>
</head>
<body>
<!--准备好一个容器-->
<div id="test">
</div>
<!--react的核心库-->
<script type="text/javascript" src="../js/react.development.js"></script>
<!--react的扩展库,专门用于操作DOM-->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!--引入babel,用于解析jsx语法为原生js语法-->
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/babel">
/**
* componentDidMount(). =====》常用
一般在这个钩子中做一些初始化的事,例如:开启定时器,发送网络请求,订阅消息
componentWillUnmount() ====》常用
一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息
*
* **/
class Count extends React.Component {
// 构造器
constructor(props) {
console.log('Count-constructor')
super(props)
// 初始化状态
this.state = { count: 0 }
}
//加1按钮的回调
add = () => {
// 获取原来的状态
const { count } = this.state
this.setState({ count: count + 1 })
}
// 卸载组件的回调
death = () => {
// 卸载组件unmount
ReactDOM.unmountComponentAtNode(document.getElementById('test'))
}
// 强制更新按钮的回调
force = () => {
this.forceUpdate()
}
// 组件将要挂在的钩子
componentWillMount() {
console.log('Count-componentWillMount')
}
// 组件挂载完毕的钩子
componentDidMount() {
console.log('Count-componentDidMount')
}
// 组件将要卸载的钩子
componentWillUnmount() {
console.log('Count-componentWillUnmount')
}
// 控制组件更新的阀门
shouldComponentUpdate() {
console.log('Count-shouldComponentUpdate')
// 不写返回值,默认为true
// 如果是true,每次更新state的时候,都会调用一次render,如果是false,包含render在内的3个函数都不会调用
return true
}
// 组件将要更新的钩子
componentWillUpdate() {
console.log('Count-componentWillUpdate')
}
// 组件更新完毕的钩子
componentDidUpdate() {
console.log('Count-componentDidUpdate')
}
render() {
console.log('render')
const { count } = this.state
return (
<div>
<h2>当前求和为{count}</h2>
<button onClick={this.add}>点我+1</button>
<button onClick={this.death}>点我卸载</button>
<button onClick={this.force}>不修改任何状态中的数据,强制更新一下</button>
</div>
)
}
}
// 父组件A
class A extends React.Component {
// 初始化状态
state = { carName: '奔驰' }
changeCar = () => {
this.setState({ carName: '宝马' })
}
render() {
return (
<div>
<div>我是A组件</div>
<button onClick={this.changeCar}>换车</button>
<B carName={this.state.carName} />
</div>
)
}
}
// 子组件B
class B extends React.Component {
componentDidMount() {
console.log("B---componentDidMount")
}
// 组件将要接收props调用,(第一次不调用,之后调用)
componentWillReceiveProps() {
console.log("B---componentWillReceiveProps", this.props)
}
// 控制组件更新的阀门
shouldComponentUpdate() {
console.log('B-shouldComponentUpdate')
return true
}
// 组件将要更新的钩子
componentWillUpdate() {
console.log('B-componentWillUpdate')
}
// 组件更新完毕的钩子
componentDidUpdate() {
console.log('B-componentDidUpdate')
}
render() {
console.log('B-render')
return (
<div>我是B组件,接收到的车是:{this.props.carName}</div>
)
}
}
// 渲染组件,实际上就是挂载(mount),
ReactDOM.render(<Count />, document.getElementById('test'))
</script>
</body>
</html>
生命周期的三个阶段(旧)
初始化阶段: 由ReactDOM.render()触发—初次渲染
1.constructor()
2.componentWillMount()
3.render()
4.componentDidMount(). =====》常用
一般在这个钩子中做一些初始化的事,例如:开启定时器,发送网络请求,订阅消息
更新阶段: 由组件内部this.setSate()或父组件重新render触发
1.shouldComponentUpdate()
2.componentWillUpdate()
3.render()
4.componentDidUpdate()
卸载组件: 由ReactDOM.unmountComponentAtNode()触发
1.componentWillUnmount() ====》常用
一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息
生命周期流程图(新)
新版本和旧版本相比,废弃了componentWillMount、componentWillReceiveProps、componentWillUpdate,提出了两个新的getDerivedStateFromProps、getSnapshotBeforeUpdate方法
初始化阶段: 由ReactDOM.render()触发—初次渲染
1.constructor()
2.getDerivedStateFromProps
3.render()
4.componentDidMount()
更新阶段: 由组件内部this.setSate()或父组件重新render触发
1.getDerivedStateFromProps
2.shouldComponentUpdate()
3.render()
4.getSnapshotBeforeUpdate
5.componentDidUpdate()
卸载组件: 由ReactDOM.unmountComponentAtNode()触发
1.componentWillUnmount()
重要的勾子
1.render:初始化渲染或更新渲染调用
2.componentDidMount:开启监听, 发送ajax请求
3.componentWillUnmount:做一些收尾工作, 如: 清理定时器
即将废弃的勾子
1.componentWillMount
2.componentWillReceiveProps
3.componentWillUpdate
现在使用会出现警告,下一个大版本需要加上UNSAFE_前缀才能使用,以后可能会被彻底废弃,不建议使用。