什么是生命周期
类似以下代码:
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也有这些过程,称之为生命周期!
React的生命周期
函数列表:
constructor()
static getDerivedStateFromProps() //
shoudComponentUpdate()
render()
getSnapshotBeforeUpdate() //
componentDidMount()
componentDidUpdate()
componentWillUnmount()
static getDerivedStateFromError() //
componentDidCatch() //
其中第2、5、9、10行的函数用不到了。
constructor
用途:
- 初始化 props
- 初始化 state,但此时不能调用setState
- 用来写bind this,如
可以用新语法代替:constructor(){
/*其他代码略*/
this.onClick = this.onClick.bind(this)
}
onClick = () => {}
constructor(){/*其他代码略*/}
render
用途:
- 展示视图,展示返回的东西,如
return (<div>...</div>)
。 - 只能有一个根元素。即返回来的东西最外层的标签只有一个,如:
render(){
return (
<div> //最外层只有一个div标签
{this.state.n}
<button onClick={this.onClick}> +1 </button>
</div>
)
}
如果想有两个div标签,但又不想把这两个标签放在一个div里返回。可以用:<React.Fragment>...</React.Fragment>
,把两个标签放进这里面即可。但是,因为太长了,可以选择缩写的<>...</>
。效果是一样的,如:
render(){
return (
<React.Fragment>
....
</React.Fragment>
)
}
//下面是简写版
render(){
return (
<>
....
</>
)
}
小技巧
render里面可以写
if...else
render(){
let message;
if(this.state.n % 2 === 0){
message - <div>偶数</div>;
}else {
message - <div>奇数</div>;
}
return {
<>
{message}
<button onClick={this.addM}>+1</button>
</>
}
}
render里面可以写
?:表达式
render(){
return {
<>
{this.state.n % 2 === 0 ? <div>偶数</div> : <div>奇数</div>}
<button onClick={this.addM}>+1</button>
</>
}
}
render里面不能直接写for循环,需要用数组。
- render里面可以写array.map(循环)
constructor(props){
super(props);
this.state = {
arr: [1, 2, 3]
}
}
render(){
return this.state.arr.map((n) => <div key={n}> {n} </div>) //map是操作数组的写法
}
shouldComponentUpdate
用途:
- 返回true,表示不阻止UI更新。
- 返回false相反,表示阻止UI更新。
因为React不像Vue一样会自动更新UI,需要手动更新。换句话说,这个函数可以允许手动是否需要进行组件更新,可以灵活地设置返回值,以避免不必要的更新。如:
shouldComponentUpdate(newProps, newState){
if(this.state.n === newState.n){
return false;
} else {
return true;
}
}
addN = () => {
this.setState((state) => ({n: state.n + 1}));
this.setState((state) => ({n: state.n - 1}));
}
render(){
console.log('N变化了一次');
return (
<div>
hi,{this.state.n}
<button onClick={this.addN}>+1</button>
</div>
)
}
上面代码,按下按钮表示先+1再-1,也就是数字并没有变化,如果不设置 shouldComponentUpdate
函数的话, 每按下一次,render
就不断渲染。设置了 shouldComponentUpdate
,旧值与新值并没有变化,也就不会再渲染!
:::info
面试:shouldComponentUpdate有什么用?
它允许我们手动判断是否要进行组件更新,我们可以根据应用场景灵活地设置返回值,以避免不必要的更新。
:::
React.PureComponent
React.PureComponent
可以代替上面shouldComponentUpdate
,但是这个只能识别第一层对象。 PureComponent
会在 render
之前对比新state和旧的state的每一个key,以及新的props和旧props的每一个key。 如果所有的key的值全都一样,就不会render,反之就会render。
class App extends React.PureComponent{
/*略*/
}
:::tips
注意:不管如何,如果不用返回的时候,都要返回一个 true,即 return true
。不然不返回true或false的话会自动返回一个undefined。
:::
conponentDidMount
用途:
- 在元素插入页面后执行代码,这些代码依赖DOM,比如想获取div的高度,就最好在这里写。
- 此处可以发起加载数据的AJSX请求(官方推荐)
- 首次渲染会执行此钩子。
class App extends React.PureComponent{
constructor(props) {
super(props);
this.state = {
n: 1
}
}
addN = () => {
this.setState((state) => ({n: state.n + 1}))
}
conponentDidMount(){
let div = document.getElementById('xxx');
let {width} = div.getBoundingClientRect();
this.setState({width})
}
render(){
return <div id='xxx'>你好,我的width为:{this.state.width}px</div>
}
}
结果如下:
或者改良代码:
class App extends React.PureComponent{
divRef = undefined;
constructor(props) {
super(props);
this.state = {
n: 1
}
this.divRef = React.createRef()
}
addN = () => {
this.setState((state) => ({n: state.n + 1}))
}
conponentDidMount(){
let div = this.divRef.current;
let {width} = div.getBoundingClientRect();
this.setState({width})
}
render(){
return <div ref={this.divRef}>你好,我的width为:{this.state.width}px</div>
}
}
componentDidUpdate
用途:
- 在视图更新后执行代码。
- 此处也可以发起AJAX请求,用于更新数据。
- 首次渲染不会执行此钩子
注意:
- 此处的setState可能会引起无限循环,除非放在if语句里。
- 若
shouldComponentUpdate
返回false,则不会触发此钩子。
componentWillUnmount
用途:
- 组件将要被移出页面然后被销毁时执行代码。
- unmount过的组件不会再次mount了。
例如:
- 如果在
componentDidMount
里面监听了window scroll,那么就要在componentWillUnmount
里面取消监听。 - 如果在
componentDidMount
里面创建了Timer,那么就要在componentWillUnmount
里面取消Timer。 - 如果在
componentDidMount
里面创建了AJAX请求,那么就要在componentWillUnmount
里面取消请求。 - 即,谁污染谁治理。
以上必须知道,否则只能说明是个菜鸟。
总结
最后,放一张生命周期图