创建方式
两种方式创建 Class 组件
ES5方式(过时)
import React from 'react'
const A = React.createClass({
render() {
return(
<div>hi</div>
)
}
})
export default A
// 由于 ES5 不支持 class,才会有这种方式
ES6方式
import React from 'raeact'
class B extends React.Component {
constructor(props){
super(props)
}
render(){
return(
<div>hi</div>
)
}
}
export default B
对比
ES6 的 class 方式更好
如果浏览器不支持ES6(IE8),用webpack+babel将ES6翻译为ES5即可
Props 外部数据
传入props给B组件
class Parent extends React.Component {
constructor(props){
super(props)
this.state = {name: 'river'}
}
onClick = () => {}
render(){
return <B name={this.state.name}
onClick={this.onClick}>hi</B>
}
}
// 外部数据被包装为一个对象
// {name: 'river', onClick:..., children: 'hi'}
// 此处的 onClick 是一个回调
初始化
class B extends React.Component {
constructor(props){
super(props);
}
render(){}
}
要么不初始化,即不写constructor
要么初始化,且写全套
效果:这么做之后,this.props就是外部数据对象的地址了
读取props
class B extends React.Component {
constructor(props) {
super(props);
}
render(){
return <div onClick={this.props.onClick}>
{this.props.name}
<div>
{this.props.children}
</div>
</div>
}
}
// 通过 this.props.xxx 读取
写props
改props的值(一个地址)
this.props = {/*另一个对象*/}
注意:这样写的代码没有意义,既然是外部数据,就应该由外部更新
改props的属性
this.props.xxx='hi'
注意:这样写的代码没有意义,既然是外部数据,就应该由外部更新
原则
应该由数据的主人对数据进行更改
相关钩子
componentWillReceiveProps 钩子
当组件接受新的props时,会触发此钩子
钩子即“特殊函数”
该钩子已经被弃用
更名为 UNSAFE_componentWillReceiveProps
Props的作用
接受外部数据
只能读不能写,外部数据由父组件传递
接受外部函数
在恰当的时机调用该函数,该函数一般是父组件的函数
State & setState
初始化State
class B extends React.Component{
constructor(props){
super(props);
this.state = {
user: {name: 'river', age: 18}
}
}
render() {/* ... */}
}
读写State
读用 this.state
this.state.xxx.yyy.zzz
写用 this.setState(???,fn)
this.setState(newState,fn)
// 注意 setState 不会立刻改变 this.state,会在当前代码运行完成后,再去更新 this.state,从而触发 UI 更新
this.setState((state,props)=> newState,fn)
// 这种方式的 state 反而更易于理解
// fn 会在写入成功后执行
写时会 shallow merge
setState会自动将新state与旧state进行一级合并
直接修改 this.state 的属性值
this.state n += 1
this.setState(this.state)
可以实现,但不推荐使用
生命周期 Lifecycle
生命周期
类比如下代码
let div = document.createElement('div')
// 这是 div 的create / construct 过程
div.textContent = 'hi'
// 这是初始化 state
document.body.appendChild(div)
// 这是 div 的 mount 过程
div.textContent = 'hi2'
// 这是 div 的 update 过程
div.remove()
// 这是 div 的 unmount 过程
同理
React组件也有这些过程,我们称之为生命周期
函数列表
constructor()在这里初始化 statestatic getDerivedStateFromProps()shouldComponentUpdate()return true / false 更新 / 阻止更新render()创建虚拟DOMgetSnapshotBeforeUpdate()componentDidMount()组件已出现在页面componentDidUpdate()组件已更新componentDidUnmount()组件将死static getDerivedStateFromError()componentDidCatch()
constructor
用途:
-
初始化
props -
初始化
state,但此时不能调用setState -
用来写
bind thisconstructor(){ /* 其他代码略 */ this.onClick = this.onClick.bind(this) } // 可以用新语法代替 onClick = () => {} constructor(){ /*其他代码略*/ } -
可不写
shouldComponentUpdate
用途:
-
返回 true 表示不阻止 UI 更新
-
返回 false 表示阻止 UI 更新
-
由上面示例得到一些启发——可以将
newState和this.state的每个属性都对比一下。如果全都相等,就不更新,如果一个不等,就更新。实际上 React 内置了这个功能,示例
React.PureComponent会在 render 之前对比新 state 和旧 state 的每一个 key,以及新 props 和旧 props 的每一个 key。如果所有 key 的值全都一样,就不会 render;如果有任何一个 key 的值不同,就会 render。
注意这个对比只是一层(浅对比)。
render
用途:
-
展示视图
return (<div>...</div>) -
只能有一个根元素
-
如果有两个根元素,就要用
<React.Fragment>内容</React.Fragment>,可以缩写成<></>
使用<React.Fragment/>或<></>,相当于一个占位符,渲染后就不显示了
而如果用div></div>包裹,这层div是一直显示的
技巧:
- render 里面可以写
if...else - render 里面可以写
?:表达式 - render 里面可以写
&&和||表达式 - render 里面不能直接写
for循环,需要用数组 - render 里面可以写
array.map(循环)
componentDidMount()
用途:
-
在元素插入页面后执行代码,这些代码依赖DOM
比如像获取div的宽度,就最好在这里写
-
此处可以发起加载数据的
AJAX请求(官方推荐) -
首次渲染会执行此钩子
关于上面代码中用到的Ref
componentDidUpdate()
用途:
- 在视图更新后执行代码
- 此处也可以发起
AJAX请求,用于更新数据(看文档) - 首次渲染不会执行此钩子
- 在此处
setState可能会引起无限循环,除非放在if里 - 若
shouldComponentUpdate返回false,则不触发此钩子
参数:
componentDidUpdate(prevProps, prevState, snapshot)
componentWillUnmount
用途:
- 组件将要被移出页面然后被销毁时执行代码
unmount过的组件不会再次mount
举例:
如果在c..DidMount里面监听了window.scroll,那么就要在c..WillUnmount里面取消监听
如果在c..DidMount里面创建了Timer,那么就要在c..WillUnmount里面取消Timer
如果在c..DidMount里面创建了AJAX请求,那么就要在c..WillUnmount里面取消请求