「React」一定要掌握的6个生命周期!

235 阅读3分钟

前言

类比以下代码

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

同理

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

一、constructor

在这里初始化 state

二、shouldComponentUpdate

是否需要更新组件

用途

  • 它允许我们手动判断是否要进行组件更新,我们可以根据应用场景灵活地设置返回值,以避免不必要的更新(让我们自己确定是否更新)
  • 返回 true 表示不阻止 UI 更新
  • 返回 false 表示阻止 UI 更新

示例

 class App extends React.Component {
   constructor(props) {
     super(props)
     this.state = { n: 1 }
   }
   onClick = () => {
     // onClick 函数里对 n 进行了 +1 -1 操作
     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  // 返回 false,表示阻止 render
     } else {
       return true  // 返回 true,表示不阻止 render
     }
   }
   render() {
     console.log('render了') // 不加 shouldComponentUpdate 的时候 log 一下看看?
     return (
       <div>
         n: {this.state.n}
         <button onClick={this.onClick}>+1</button>
       </div>
     )
   }
 }

使用内置 React.PureComponent

使用 React.PureComponent 代替 React.Component

PureComponent 会在 render 之前对新旧 state 进行对比(之对比第一层,浅对比),如果对比结果一样,那就不更新

 class App extends React.PureComponent {
   constructor(props) {
     super(props)
     this.state = { n: 1 }
   }
   onClick = () => {
     // onClick 函数里对 n 进行了 +1 -1 操作
     this.setState(state => ({ n: state.n + 1 }))
     this.setState(state => ({ n: state.n - 1 }))
   }
   render() {
     console.log('render了')
     return (
       <div>
         n: {this.state.n}
         <button onClick={this.onClick}>+1</button>
       </div>
     )
   }
 }

三、render

创建虚拟 DOM

用途

  • 展示视图

    return (<div>...</div>) 这是虚拟 DOM

  • 只能有一个根元素

  • 如果有两个根元素,就要用 <React.Fragment> 包起来

  • <React.Fragment> 可以缩写成<></>

示例

 class App extends React.PureComponent {
   constructor(props) {
     super(props)
     this.state = { n: 1 }
   }
   onClick = () => {
     this.setState(state => ({ n: state.n + 1 }))
   }
   render() {
     return (
       <>
         n: {this.state.n}
         <button onClick={this.onClick}>+1</button>
       </>
     )
   }
 }

可以写 if...else...

&& 也可以,|| 也可以

 class App extends React.PureComponent {
   constructor(props) {
     super(props)
     this.state = { n: 1 }
   }
   onClick = () => {
     this.setState(state => ({ n: state.n + 1 }))
   }
   render() {
     return (
       <>
         {this.state.n % 2 === 0 ? <div>偶数</div> : <div>奇数</div>}
         <button onClick={this.onClick}>+1</button>
       </>
     )
   }
 }

可以写 map

不可以直接写 for 循环,需要用数组,可以用 map 来代替

 class App extends React.PureComponent {
   constructor(props) {
     super(props)
     this.state = { array: [1, 2, 3] }
   }
   render() {
     return this.state.array.map(n => <div key={n}>{n}</div>)
   }
 }

记得要写 key

四、componentDidMount

组件已出现在页面

用途

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

五、componentDidUpdate

组件已更新

用途

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

六、componentWillUnmount

组件将要被卸载

用途

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

举例

  • 如果在 componentDidMount 里监听了 window scroll,那么就要在 componentWillUnmount 里取消监听
  • 如果在 componentDidMount 里创建了 Timer,那么就要在 componentWillUnmount 里取消 Timer
  • 如果在 componentDidMount 里创建了 AJAX 请求,那么就要在 componentWillUnmount 里取消请求
  • 否则前端页面的内存会很多
  • 原则:谁污染谁整治

能用函数组件就用函数组件,别用 class

完。