React笔记(7):React钩子函数

73 阅读5分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天,点击查看活动详情

-钩子函数

钩子函数,也称生命周期函数,是描述一个组件从创建到销毁的全部过程。基于钩子函数,在组件执行的过程中(创建前|创建后|二次渲染前|组件销毁...)可以加入我们自己的逻辑和操作

【组件渲染的基本流程触发的钩子】

  1. constructor 创建组件(构造函数)

  2. componentWillMount 组件第一次渲染前

    在这个钩子函数中的this.setState会变成同步操作

  3. render 第一次渲染(返回组件)

  4. componentDidMount 组件第一次渲染后

    一般在这个钩子函数中进行ajax数据请求, 页面数据绑定这些操作

【组件状态数组发生改变setState|作为子组件其属性改变时(子组件的属性是父组件状态)】

  1. shouldComponentUpdate 是否允许组件重新渲染(返回True允许执行再次渲染操作)

    可以在这个钩子函数中进行判断, 是否允许一个组件进行二次|多次渲染, 进行性能优化

  2. componentWillUpdate 组件重新渲染前

  3. render 第二次及后续所有渲染触发

  4. componentDidupdate 组件重新渲染后

【父组件把传递给子组件的属性发生改变后触发】

  1. componentWillReceiveProps

    作为子组件的时候才有, 子组件的属性是父组件状态, 父组件状态发生改变, 子组件属性也对应改变, 重新渲染组件触发该钩子

【组件被卸载前】

  1. componentWillUnmount 组件卸载前触发

    卸载:原内容在页面中不会消失,只是取消了其数据驱动特性,变成静态组件(死内容)

-代码实践

把每一个钩子函数都过一遍,光说不练假把式,一定要自己实践一遍

1.钩子触发流程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9cKWNeAZ-1602418168682)(React.assets/.png)]

class LifeFunction extends React.Component {
  static defaultProps = {}     // 第一步赋值默认值

  constructor(props, context) {
    super(props, context)
    console.log('Constructor()  \t\t  1. 创建组件(构造函数)')

    this.state = {
      time: new Date().toLocaleString(),
      n: 1
    }
  }

  render() {
    console.log('render() \t\t   组件渲染')
    let { time, n } = this.state

    return <section>
      <p onClick={this.change}>时间: {time}</p>
      <p>n的值: {n}</p>
    </section>
  }

  UNSAFE_componentWillMount(...args) {
    /**
     *  this.setState({
     *    n: 2
     *  })
     * 在componentWillMount, 如果直接使用this.setState进行状态修改, 
     *    1. 不会触发componentUpdate
     *    2. 这里的执行顺序很特殊, this.setState会在该钩子函数中同步执行
     *    3. 也就是说会先进行状态的修改, 后进行第一次渲染
     */
    // this.setState({
    //   n: 2
    // })
    console.log('componentWillMount()      2.组件第一次渲染前')
  }

  componentDidMount() {
    console.log('componentDidMount()       3. 组件第一次渲染后')
  }

  shouldComponentUpdate(nextProps, nextStates) {
    /**
     *  接受两个参数:
     *    nextStates: 新的状态值
     *    nextProps: 新的属性值
     * 在这个构造函数中, 如果用this.state获取状态值, 得到的是还没有被修改的状态值
     *   例如:有个this.state中有个状态值n为1, 在第一次渲染之后过2秒状态被修改成2, 触状态修改->React再次渲染
     *      就会触发该钩子函数, 在这个钩子函数中获取的状态值为旧的状态值, 等到真正第二次渲染之后this.state才会变成新的
     */

    // nextProps | nextStates 最新修改的属性|状态
    console.log('n', this.state.n)
    console.log(nextStates) // 要改变的状态
    console.log('shouldComponentUpdate4()  4. 组件状态改变触发,返回True允许重新渲染')
    return true
  }

  UNSAFE_componentWillUpdate(nextProps, nextStates) {
    /**
     *  接受两个参数:
     *    nextStates: 新的状态值
     *    nextProps: 新的属性值
     * 在这个构造函数中, 如果用this.state获取状态值, 得到的是还没有被修改的状态值
     *   例如:有个this.state中有个状态值n为1, 在第一次渲染之后过2秒状态被修改成2, 触状态修改->React再次渲染
     *      就会触发该钩子函数, 在这个钩子函数中获取的状态值为旧的状态值, 等到真正第二次渲染之后this.state才会变成新的
     */
    console.log('n', this.state.n)
    console.log(nextStates) // 要改变的状态
    console.log('componentWillUpdate()     5. 组件重新渲染前')
  }

  componentDidUpdate(prevProps, prevStates) {
    /** 
     *  接收两个值: 
     *    prevProps :   被更新(旧的)的属性
     *    prevStates  : 被更新的(旧的)状态
     */
    console.log('n', this.state.n)
    console.log(prevStates) // 被改变的状态
    console.log('componentDidUpdate() \t  6. 组件重新渲染后')
  }

  UNSAFE_componentWillReceiveProps() {
    console.log('我被执行了')
  }

  change = (e) => {
    this.setState({
      time: new Date().toLocaleString(),
      n: 2
    })
  }

  componentWillUnmount() {
    console.log('componentWillUnmount()  组件即将被卸载')
  }
}

2.子组件属性改变

组件的属性是只读的, 这里改变子组件的属性是指父组件嵌套子组件时,父组件把其状态作为属性传入子组件中,当父组件的状态改变时,会触发父组件的二次渲染,因为父组件的状态是子组件的属性,所以子组件的属性也会改变并重新渲染子组件,并触发子组件的 componentWillReceiveProps

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zETjvzrl-1602418168684)(React.assets/)]

class A extends React.Component {
  constructor() {
    super()
  }

  UNSAFE_componentWillUpdate(nextProps, nextStates) {
    console.log('属性被修改触发')
    console.log(nextProps)
  }

  render() {
    return <div>
      <p>{this.props.value}</p>
    </div>
  }

  shouldComponentUpdate() {
    console.log('是否进行更新')
    return true
  }

  UNSAFE_componentWillUpdate(nextProps, nextStates) {
    console.log('子组件重新渲染前')
    console.log('新的值:', nextProps)
  }

  componentDidUpdate(prevProps, prevStates) {
    console.log('子组件重新渲染后')
    console.log('旧的值:', prevProps)
  }

  UNSAFE_componentWillReceiveProps(nextProps, nextStates) {
    // 子组件的属性是父组件的状态, 父组件状态发生改变, 子组件重新渲染, 触发该函数
    console.log('子组件属性被修改')
    console.log('新的值:', nextProps)
  }
}

class B extends React.Component {
  constructor() {
    super()

    this.state = {
      value: 'B的值'
    }
  }

  render() {
    // 复合组件, 组件嵌套(套娃)
    return <div>
      {/* 把父组件的状态信息作为属性传递给子组件 */}
      <A value={this.state.value}></A>
    </div>
  }

  componentDidMount() {
    setTimeout(() => {
      this.setState({
        value: '修改了值'
      })
    }, 2000)
  }
}

ReactDOM.render(
  <section>
    <B></B>
  </section>,
  root
)

-官方流程图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xADldeoC-1602418168686)(React.assets/image-.png)]

-重命名问题

  • componentWillMount
  • componentWillReceiveProps
  • componentWillUpdate

这三个生命周期函数倾向于去鼓励编写不安全的代码, 这几个异步钩子函数会造成一些潜在的BUG,所以官方让我们用这三个钩子的时候加上 UNSAFE_ 前缀, 比如 UNSAFE_componentWillMount, 如果没有也没关系, 控制台会抛出一个警告, 官方也推荐让我们把抓取数据的代码转移到 componentDid 开头的函数里

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5P9lTd4p-1602418168688)(React.assets/image-.png)]