React类组件生命周期

123 阅读3分钟

生命周期

生命周期类似如下代码:

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

React组件也有这些过程,我们称为生命周期。

我们主要了解以下几个生命周期:
constructor()——在这里初始化state
shouldComponentUpdate()——return false 阻止更新
render()——创建虚拟DOM
componentDidMount()——组件已出现在页面
componentDidUpdate()——组件已更新
componentWillUnmount()——组件将死

constructor

用途

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

shouldComponentUpdate

用途

  • 返回true表示不阻止UI更新
  • 返回false表示阻止UI更新

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

我们来个例子吧,结合例子和以上内容会更容易理解,情景如下:
我们定义一个函数onClick,对n进行+1和-1操作,如此,页面上的n是不会变的,那每次调用onClick会重新render吗?答案是会的。代码如下:

export default class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      n :1
    };
  }
  onClick = () => {
    this.setState(state => ({
      n: state.n+1
    }));
    this.setState(state => ({
      n: state.n-1
    }))
  };
  render(){
    console.log('render了一次')
    return (
      <div>
       {this.state.n}
       <button onClick={this.onClick}>+1-1</button>
      </div>
    )
  }
}

渲染的结果如下(注意看log)

react1.gif

那如何让页面上的值不变就不会重新render呢? 用sholdComponentUpdate,将newState和this.state的每个属性都对比一下,如果全部相等就不更新,如果有一个不相等,就更新

  constructor(props) {
    super(props);
    this.state = {
      n:1
    }
  }
  onClick = ()=>{
    this.setState(state=>({n:state.n+1}))
    this.setState(state=>({n:state.n-1}))
  }
  shouldComponentUpdate(newProps,newState){
    if(newState.n === this.state.n){
      return false
    }else{
      return true
    }
  }
  render(){
    console.log('render了一次');
    return (
      <div>
        {this.state.n}
        <button onClick={this.onClick}>+1-1</button>
      </div>
    )
  }
}
 

结果是:不更新就不重新render,结果如下:

react2.gif
基于此特点,React内置了这个功能,这个功能叫React.PureComponet来代替React.Component,可以实现和sholdComponentUpdate一样的功能,代码如下:

export default class App extends React.PureComponent{
  constructor(props){
    super(props);
    this.state={
      n:1
    }
  }
  onClick = () =>{
   this.setState(state=>({n:state.n+1}))
   this.setState(state=>({n:state.n-1}))
  }
  render(){
    console.log('render了');
    return(
      <div>
        {this.state.n}
        <button onClick={this.onClick}>+1-1</button>
      </div>
    )
  }
}

render

用途

  • 用于展示视图: return (<div>...</div>)
  • 只能有一个根元素
  • 如有两个根元素,用<React.Fragment>包起,<React.Fragment/>可缩写为<></>
    技巧
  • render里面可以写if...else
  • render里可以写?:表达式
  • render里不能直接写for循环,需要用数组,如下:
export default class App extends React.Component{
  constructor(props){
    super(props)
    this.state = {
      n:1,
      array: [1,2,3,4,5]
    }
  }
  render(){
     let result = []
     for (let i=0;i<this.state.array.length;i++){
       result.push(this.state.array[i])
     }
     return result
  }
}
  • render里可以写array.map(循环)
class App extends React.Component{
  constructor(props){
    super(props)
    this.state = {
      n:1,
      array: [1,2,3,4,5]
    }
  }
  render(){
   return this.state.array.map(n=><span key={n}>{n}</span>)
 }
}
export default App;

componentDidMount()

用途

  • 在元素插入页码后执行代码,这些代码依赖DOM
  • 比如想要获取div的高度,最好在这里写
  • 此处可以发起加载数据的AJAX请求(官方推荐)
  • 首次渲染会执行此钩子

componentDidUpdate()

用途

  • 视图更新后执行代码
  • 此处也可以发起AJAX请求,用于更新数据
  • 首次更新不会执行此钩子
  • 在此处setState可能会引起无限循环,除非放在if里
  • 若shouldComponentUpdate返回false,则不会触发此钩子

componentWillUnmount

用途

  • 组件将要被移出页面然后被销毁时执行代码
  • unmount过的组件不会再次mount
    举例
  • 如果在componentDidMount里面监听了window.scroll,那么就要在componentWillUnmount里面取消监听
  • 如果在componentDidMount里面创建了Timer,那么就要在componentWillUnmount里面取消Timer
  • 如果在componentDidMount里面创建了AJAX请求,那么就要在componentWillUnmount里面取消AJAX请求
    最后通过一张图回顾各钩子执行顺序

image.png