两种方式创建Class组件
ES5方式(过时)
import React from 'react'
const Demo = React.createClass({
render(){
return(<div>hello lucidity</div>)
}
})
export default Demo
ES6方式(推荐使用)
import React from 'react' 1
class Demo extends React.Component{ 2
constructor(props){ 3
super(props) 4
} 5
render(){
return(<div>hello lucidity</div>)
}
}
export default Demo
第3,4,5行代码可写可不写,当这三行代码中没有其它的代码时可不写,反之则要写
以上都是在项目中单文件组件的书写方式,在学习中还是使用CDN的引入方式
Class中props的使用(外部数据)
props的简单实用
class App extends React.Component {
render() {
return (
<div>hello {this.props.name}</div>
);
}
}
const rootElement = document.getElementById("app")
ReactDOM.render(<App name="frank" />, rootElement);
输出:hello frank
props的批量传递
class App extends React.Component {
render() {
const {name,age,sex} = this.props 使用解构赋值拿出name,age,sex
return (
<ul>
<li>{name}</li>
<li>{age}</li>
<li>{sex}</li>
</ul>
);
}
}
const demo = {name: '坤坤',age: 28,sex: '男'} 定义demo等于这些对象
const rootElement = document.getElementById("app")
ReactDOM.render(<App {...demo} />, rootElement) 直接用{...demo}表示demo
页面中呈现的内容是:
对props进行限制
还是上面那段代码,只不过要对props进行限制:
- name是必传的,且为字符串
- age可传可不传,不传的话默认为18
- sex可传可不传,不传的话默认为男
要想限制props,需要引入prop-types
这个库,这个库的就是对组件标签属性进行限制
CDN引入:<script src="https://cdn.bootcdn.net/ajax/libs/prop-types/15.7.2/prop-types.js"></script>
propTypes
要写在render函数的上面:
static propTypes = {
}
name是必传的,且为字符串,代码为
name: PropTypes.string.isRequired
age可传可不传,不传的话默认为18,代码为
age: PropTypes.number,
sex: PropTypes.string
代码为
static propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number,
sex: PropTypes.string
}
通过propTypes
,我们确实可以对外部数据进行限制,可是却无法设置默认值,想要设置默认值,需要用到dafaultProps
,这个不再需要引入,因为在引入React的时候已经引入了
如何使用dafaultProps
static dafaultProps = {
}
对age设置默认值
age: 18
对sex设置默认值
sex: '男'
代码为
static defaultProps = {
age: 18,
sex: '男'
}
完整代码为
<!DOCTYPE html>
<html>
<head>
<title>Parcel Sandbox</title>
<meta charset="UTF-8" />
</head>
<body>
<div id="app"></div>
<script src="https://cdn.bootcdn.net/ajax/libs/react/17.0.1/umd/react.development.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/react-dom/17.0.1/umd/react-dom.development.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/babel-standalone/7.0.0-beta.3/babel.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/prop-types/15.7.2/prop-types.js"></script>
<script type="text/babel">
class App extends React.Component {
static propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number,
sex: PropTypes.string
}
static defaultProps = {
age: 18,
sex: '男'
}
render() {
const {name,age,sex} = this.props
return (
<div>
<ul>
<li>{name}</li>
<li>{age}</li>
<li>{sex}</li>
</ul>
</div>
);
}
}
const demo = {name: 'lucidity',age: 28,sex: '男'}
const rootElement = document.getElementById("app")
ReactDOM.render(<App {...demo} />, rootElement);
</script>
</body>
</html>
页面中显示为
注意:props只支持读,不支持写,父组件传给子组件的东西,应该由父组件自己修改,而不是通过子组件来修改
Class中state的使用(内部数据)
对state进行初始化
constructor(props){
super(props)
this.state = {
n: 0 初始化n为0
}
}
修改state的值用setState
add = () => {
this.setState((state)=> ({n: this.state.n + 1}))
}
点击button+1的案例
<!DOCTYPE html>
<html>
<head>
<title>Parcel Sandbox</title>
<meta charset="UTF-8" />
</head>
<body>
<div id="app"></div>
<script src="https://cdn.bootcdn.net/ajax/libs/react/17.0.1/umd/react.development.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/react-dom/17.0.1/umd/react-dom.development.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/babel-standalone/7.0.0-beta.3/babel.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/prop-types/15.7.2/prop-types.js"></script>
<script type="text/babel">
class App extends React.Component {
constructor(props){
super(props)
this.state = {
n: 0
}
}
add = () => {
this.setState((state)=> ({n: state.n + 1}))
}
render() {
return (
<div>
{this.state.n}
<button onClick = {this.add}>+1</button>
</div>
);
}
}
const rootElement = document.getElementById("app")
ReactDOM.render(<App/>, rootElement);
</script>
</body>
</html>
生命周期
类比如下代码
let div = document.createElement('div')
- 这是div的create(创建)过程
div textContent = 'hi'
- 这是初始化state
document.body.appendChild(div)
- 这是div的mount(挂载)过程
div textContent = 'hello'
- 这是div的update(更新)的过程
div.remove()
- 这是div的卸载过程
同理,react组件也有这些过程,我们称之为生命周期
函数列表
因为在创建或者更新组件的时候,会触发某些函数,而这些函数被叫做生命周期
constructor()
初始化state
shouldComponentUpdate()
return false 阻止更新
render()
创建虚拟DOM
componentDidMount
组件已经出现在页面
componentDidUpdate
组件已更新
componentDidUnmount
组件将被卸载
constructor()
作用:
- 初始化props,
- 初始化state,但此时并不能调用setState
- 用来写bind(绑定) this
constructor() {
/*.......*/
this.onClick = this.onClick.bind(this)
}
绑定this,或者用下面的新语法箭头函数代替
onClick = () => {}
shouldComponentUpdate()
是否应该更新组件
用途:
- 返回true表示不阻止UI更新
- 返回false表示阻止UI更新
为什么要阻止UI更新呢?首先我们看一下vue和react的区别,分别用vue和react实现点击button+1的函数(n都为0)
vue:
add(){
this.n += 1
}
react:
add = () => {
this.setState(state=> ({n: state.n + 1}))
}
vue:点击之后,0+1 = 1,之前0的对象的内存地址 === 现在1的对象的内存地址
react:点击之后,0+1 = 1,之前0的对象的内存地址 !== 现在1的对象的内存地址,
一句话总结,vue+1操作之后,不会产生新的对象,而react+1之后,会产生新的对象
明白vue和react的这个区别以后,看下面的代码
<!DOCTYPE html>
<html>
<head>
<title>Parcel Sandbox</title>
<meta charset="UTF-8" />
</head>
<body>
<div id="app"></div>
<script src="https://cdn.bootcdn.net/ajax/libs/react/17.0.1/umd/react.development.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/react-dom/17.0.1/umd/react-dom.development.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/babel-standalone/7.0.0-beta.3/babel.min.js"></script>
<script type="text/babel">
class App extends React.Component {
constructor(props){
super(props)
this.state = {
n: 0
}
}
add = () => {
this.setState(state=> ({n: state.n + 1}))
this.setState(state=> ({n: state.n - 1}))
}
render() {
return (
<div>
{this.state.n}
<button onClick = {this.add}>+1 -1</button>
</div>
);
}
}
const rootElement = document.getElementById("app")
ReactDOM.render(<App/>, rootElement);
</script>
</body>
</html>
n为0,当点击button的时候,调用add函数,先+1,再-1,还是原来的值0,但是还会产生新的对象。值还是原来的值,但却返回了新的对象,react感觉这样不好,于是就有了shouldComponentUpdate()
函数,shouldComponentUpdate()
会检查旧的值是否等于新的值,如果改变了就更新UI,不改变就阻止更新UI。
在render
的上面添上下面几行代码
shouldComponentUpdate(newProps, newState){
if(newState.n === this.state.n) { 如果新的值 === 旧的值
return false 则不更新UI
}else {
return true 反之则更新
}
}
此时,便成功解决了上面的问题,可以打印一下看看,在render
中打印console.log('n的值变了')
,没有加上面这段代码,点一次就会打印一次,而加了上面的代码,只会打印一次(打印一次是因为初始化)
其实还有一种方法也可以实现上面的功能,将class App extends React.Component
改为class App extends React.PureComponent
render()
用途:展示视图
注意:
- 只有一个根元素
- 非要有两个根元素,用
<React.Fragment />
包起来 - render()可以写
if--else
,三元表达式,可以写array
的map
,filter
,reduce
- 不能写
for
循环
ComponentDidMount()
组件已经挂载
用途:
- 在元素插入页面后执行代码,这些代码依赖DOM
- 此处可以发起加载数据的AJAX请求
- 首次渲染会执行此钩子
举个栗子:获取div的高度height
<!DOCTYPE html>
<html>
<head>
<title>Parcel Sandbox</title>
<meta charset="UTF-8" />
<style>
div {
height: 40px; 一开始用css设置高为40px
}
</style>
</head>
<body>
<div id="app"></div>
<script src="https://cdn.bootcdn.net/ajax/libs/react/17.0.1/umd/react.development.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/react-dom/17.0.1/umd/react-dom.development.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/babel-standalone/7.0.0-beta.3/babel.min.js"></script>
<!-- <script src="https://cdn.bootcdn.net/ajax/libs/prop-types/15.7.2/prop-types.js"></script> -->
<script type="text/babel">
class App extends React.Component {
constructor(props){
super(props)
this.state = {
height: undefined 初始化height为undefined
}
this.myRef = React.createRef() 用createRef() API来标记
}
componentDidMount(){
const div = this.myRef.current 获取div
const height = div.getBoundingClientRect().height 拿到div的高
this.setState({height: height}) 设置state中的高度为上一行获取的高度
}
render(){
return (
<div ref={this.myRef}>hello lucidity {this.state.height}px</div>
)
}
}
const rootElement = document.getElementById("app")
ReactDOM.render(<App/>, rootElement);
</script>
</body>
</html>
下图是页面中呈现的效果
componentDidUpdate()
组件已经更新
用途:
- 在视图更新后执行代码
- 此处也可以发起AJAX请求,用来更新数据
- 首次渲染不会执行此钩子
- 此处setState可能会引起无限循环,除非放在if里面
- 假如shouldComponentUpdate()返回false,则不会触发此钩子
componentWillUnmount
组件将被销毁时
用途:
- 组件将要被移除页面然后被销毁时执行代码
- unmount(销毁)过的组件不会再次mount(创建,挂载)
举例:
如果你在ComponentDidMount()
里面创建了请求,那么你就要在componentWillUnmount
取消请求
如果你在ComponentDidMount()
里面创建了timer(定时器),那么你就要在componentWillUnmount
清除定时器,
因为组件都要被销毁了,创建的东西或者请求的东西都没有意义了
总结:分阶段看钩子的执行顺序
首次渲染:constructor()---------render()---------更新UI---------componentDidMount
再次渲染:(props,setState变了)---------shouldComponentUpdate---------(判断返回true或false,true结束,false继续)---------render()---------更新UI---------componentDidUpdate
销毁:componentWillUnmount