什么是生命周期
类似以下代码:
let div = document.createElement('div'); //这是div的 create/construct 的过程div.textContent = 'hi'; //这是初始化statedocument.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...elserender(){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里面取消请求。 - 即,谁污染谁治理。
以上必须知道,否则只能说明是个菜鸟。
总结
最后,放一张生命周期图

