React 学习之 Class 组件 生命周期

505 阅读3分钟

钩子函数列表

  • constructor()
  • shouldComponentUpdate()
  • render()
  • componentDidMount()
  • componentDidUpdate()
  • componentWillUnmount()

constructor

组件出现在内存

  • 组件创建后会执行该代码
  • 初始化 props
  • 初始化 state,此时不能调用 setState
  • 可写 bind this
constructor(){
this.onClick = this. onClick.bind(this)
}
可被代替为:
constructor(){}
onClick = ()=> {}

shouldComponentUpdate

是否应该更新组件

  • 返回 false 阻止 UI 更新
  • 返回 true 不阻止 UI 更新 举个例子:写一个加1之后立即减1的操作
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('变化了一次')
    return (
      <div>
        {this.state.n}
        <button onClick={this.onClick}>+1-1</button>
      </div>
    )
  }
}

你无论点击多少次页面都没有变化。而控制台一直都在 log ,即每次都在 render 。这是为什么?

  • 每次 setState 都会产生新对象,即使对象的内容相同。
  • {n:1} 和 {n:1} 不是同一个对象。
  • React 发现对象不同,将会 render 渲染组件,在DOM Diff 过程中发现新渲染的组件和旧组件内容相同,就不更新页面内容。
  • 因此页面无变化,但是 render 的确每次都执行了。
  • 如何减少不必要的 render?

使用 shouldComponentUpdate

  • 由于对象地址不同,react 认为数据变了,执行 render。
  • 该函数就是用通过新旧 state 内容是否相等来决定是否要执行 render 从而减少不必要的 render。
  • 即是否阻止 UI 更新
 shouldComponentUpdate(nextProps, nextState, nextContext) {
    if(nextState.n ===this.state.n){
      return false
    }else{
      return true
    }
  }

React 为此内置了此功能。

React.PureComponent

  • 可代替React.Component,自动检查是否更新 UI,是 React 内置的功能。
  • PureComponent 会在 render 之前对比新 state 和旧 state 的每一个 key,以及新 props 和旧 props 的每一个 key。
  • 如果所有 key 的值全都一样,就不会 render,告诉 react 不更新;如果有任何一个 key 的值不同,就会 render。
 class App extends React.PureComponent

render

创建虚拟 DOM

  • 展示视图 渲染页面
  • 只能有一个根元素
  • 若有两个根元素,要用 <React.Fragment> 包起来
  • 可用 </> 代替 <React.Fragment/>,你会发现 这只是起一个占位符作用

componentDidMount

组件已经出现在页面

  • 在元素插入页面后执行的代码,这些代码依赖 DOM
  • 页面首次渲染会执行此钩子
  • 可以发起加载数据的 AJAX 请求 举个栗子:获取 div 的宽度
class App extends React.PureComponent {
  constructor(props) {
    super(props);
    this.state = {width:undefined}
  }
  componentDidMount() {
    const div = document.getElementById('x')
    const {width} = div.getBoundingClientRect()
    this.setState({width})
  }
  render(){
    return ( <div id='x'>{this.state.width}</div>)
  }
}

由于使用 id 的重复冲突率较高,我们一般使用 ref 代替 id 获取节点

class App extends React.PureComponent {
  divRef = undefined	// 初始化
  constructor(props) {
    super(props);
    this.state = {width:undefined}![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/439a39117ae04b7b860b74ccb4dd9f99~tplv-k3u1fbpfcp-watermark.image)
    this.divRef = React.createRef()	// 创建 ref
  }
  onClick = () => {
    this.setState(state => ({n: state.n - 1}))
    this.setState(state => ({n: state.n + 1}))
  }
  componentDidMount() {
    const div = this.divRef.current	 	// 获取绑定的节点
    const {width} = div.getBoundingClientRect()
    this.setState({width})
  }
  render(){
    return (<div ref={this.divRef}>{this.state.width}</div>)	// 使用
  }
}

componentDidUpdate

组件已更新

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

conponentWillUnmount

组件将消失

  • 组件将要被移除页面然后被销毁时执行的代码
  • 例:如果在 componentDidMount 创建了 Timer,就要在这里取消了。

图示

执行顺序