什么是生命周期

类似以下代码:

  1. let div = document.createElement('div'); //这是div的 create/construct 的过程
  2. div.textContent = 'hi'; //这是初始化state
  3. document.body.appendChild(div); //这是div的mount过程
  4. div.textContent = 'hi2' //这是div的update过程
  5. div.remove() //这是div的unmount过程

上面的代码,React也有这些过程,称之为生命周期!

React的生命周期

函数列表:

  1. constructor()
  2. static getDerivedStateFromProps() //
  3. shoudComponentUpdate()
  4. render()
  5. getSnapshotBeforeUpdate() //
  6. componentDidMount()
  7. componentDidUpdate()
  8. componentWillUnmount()
  9. static getDerivedStateFromError() //
  10. componentDidCatch() //

其中第2、5、9、10行的函数用不到了。

constructor

用途:

  • 初始化 props
  • 初始化 state,但此时不能调用setState
  • 用来写bind this,如
    1. constructor(){
    2. /*其他代码略*/
    3. this.onClick = this.onClick.bind(this)
    4. }
    可以用新语法代替:
    1. onClick = () => {}
    2. constructor(){/*其他代码略*/}

render

用途:

  • 展示视图,展示返回的东西,如 return (<div>...</div>)
  • 只能有一个根元素。即返回来的东西最外层的标签只有一个,如:
    1. render(){
    2. return (
    3. <div> //最外层只有一个div标签
    4. {this.state.n}
    5. <button onClick={this.onClick}> +1 </button>
    6. </div>
    7. )
    8. }

如果想有两个div标签,但又不想把这两个标签放在一个div里返回。可以用:<React.Fragment>...</React.Fragment> ,把两个标签放进这里面即可。但是,因为太长了,可以选择缩写的<>...</>。效果是一样的,如:

  1. render(){
  2. return (
  3. <React.Fragment>
  4. ....
  5. </React.Fragment>
  6. )
  7. }
  8. //下面是简写版
  9. render(){
  10. return (
  11. <>
  12. ....
  13. </>
  14. )
  15. }

小技巧

  1. render里面可以写 if...else

    1. render(){
    2. let message;
    3. if(this.state.n % 2 === 0){
    4. message - <div>偶数</div>;
    5. }else {
    6. message - <div>奇数</div>;
    7. }
    8. return {
    9. <>
    10. {message}
    11. <button onClick={this.addM}>+1</button>
    12. </>
    13. }
    14. }
  2. render里面可以写 ?:表达式

    1. render(){
    2. return {
    3. <>
    4. {this.state.n % 2 === 0 ? <div>偶数</div> : <div>奇数</div>}
    5. <button onClick={this.addM}>+1</button>
    6. </>
    7. }
    8. }
  3. render里面不能直接写for循环,需要用数组。

  4. render里面可以写array.map(循环)
    1. constructor(props){
    2. super(props);
    3. this.state = {
    4. arr: [1, 2, 3]
    5. }
    6. }
    7. render(){
    8. return this.state.arr.map((n) => <div key={n}> {n} </div>) //map是操作数组的写法
    9. }

shouldComponentUpdate

用途:

  • 返回true,表示不阻止UI更新。
  • 返回false相反,表示阻止UI更新。

因为React不像Vue一样会自动更新UI,需要手动更新。换句话说,这个函数可以允许手动是否需要进行组件更新,可以灵活地设置返回值,以避免不必要的更新。如:

  1. shouldComponentUpdate(newProps, newState){
  2. if(this.state.n === newState.n){
  3. return false;
  4. } else {
  5. return true;
  6. }
  7. }
  8. addN = () => {
  9. this.setState((state) => ({n: state.n + 1}));
  10. this.setState((state) => ({n: state.n - 1}));
  11. }
  12. render(){
  13. console.log('N变化了一次');
  14. return (
  15. <div>
  16. hi,{this.state.n}
  17. <button onClick={this.addN}>+1</button>
  18. </div>
  19. )
  20. }

上面代码,按下按钮表示先+1再-1,也就是数字并没有变化,如果不设置 shouldComponentUpdate 函数的话, 每按下一次,render 就不断渲染。设置了 shouldComponentUpdate ,旧值与新值并没有变化,也就不会再渲染!

:::info 面试:shouldComponentUpdate有什么用?
它允许我们手动判断是否要进行组件更新,我们可以根据应用场景灵活地设置返回值,以避免不必要的更新。 :::

React.PureComponent

React.PureComponent 可以代替上面shouldComponentUpdate ,但是这个只能识别第一层对象。 PureComponent 会在 render 之前对比新state和旧的state的每一个key,以及新的props和旧props的每一个key。 如果所有的key的值全都一样,就不会render,反之就会render。

  1. class App extends React.PureComponent{
  2. /*略*/
  3. }

:::tips 注意:不管如何,如果不用返回的时候,都要返回一个 true,即 return true 。不然不返回true或false的话会自动返回一个undefined。 :::

conponentDidMount

用途:

  • 在元素插入页面后执行代码,这些代码依赖DOM,比如想获取div的高度,就最好在这里写。
  • 此处可以发起加载数据的AJSX请求(官方推荐)
  • 首次渲染会执行此钩子。
  1. class App extends React.PureComponent{
  2. constructor(props) {
  3. super(props);
  4. this.state = {
  5. n: 1
  6. }
  7. }
  8. addN = () => {
  9. this.setState((state) => ({n: state.n + 1}))
  10. }
  11. conponentDidMount(){
  12. let div = document.getElementById('xxx');
  13. let {width} = div.getBoundingClientRect();
  14. this.setState({width})
  15. }
  16. render(){
  17. return <div id='xxx'>你好,我的width为:{this.state.width}px</div>
  18. }
  19. }

结果如下:
image.png

或者改良代码:

  1. class App extends React.PureComponent{
  2. divRef = undefined;
  3. constructor(props) {
  4. super(props);
  5. this.state = {
  6. n: 1
  7. }
  8. this.divRef = React.createRef()
  9. }
  10. addN = () => {
  11. this.setState((state) => ({n: state.n + 1}))
  12. }
  13. conponentDidMount(){
  14. let div = this.divRef.current;
  15. let {width} = div.getBoundingClientRect();
  16. this.setState({width})
  17. }
  18. render(){
  19. return <div ref={this.divRef}>你好,我的width为:{this.state.width}px</div>
  20. }
  21. }

componentDidUpdate

用途:

  • 在视图更新后执行代码。
  • 此处也可以发起AJAX请求,用于更新数据。
  • 首次渲染不会执行此钩子

注意:

  • 此处的setState可能会引起无限循环,除非放在if语句里。
  • shouldComponentUpdate 返回false,则不会触发此钩子。

componentWillUnmount

用途:

  • 组件将要被移出页面然后被销毁时执行代码。
  • unmount过的组件不会再次mount了。

例如:

  • 如果在 componentDidMount 里面监听了window scroll,那么就要在 componentWillUnmount 里面取消监听。
  • 如果在 componentDidMount 里面创建了Timer,那么就要在 componentWillUnmount 里面取消Timer。
  • 如果在 componentDidMount 里面创建了AJAX请求,那么就要在 componentWillUnmount 里面取消请求。
  • 即,谁污染谁治理。

以上必须知道,否则只能说明是个菜鸟。

总结

最后,放一张生命周期图

image.png