React 组件的生命周期

Posted poki

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了React 组件的生命周期相关的知识,希望对你有一定的参考价值。

React 生命周期方法

分为四个阶段:挂载 、更新、卸载、错误处理。

挂载阶段

当组件实例插入DOM时,其生命周期的调用顺序为:

  1. construstor() 构造函数

  2. static getDerivedStateFromProps(props,state) 给派生属性更新属性

  3. render() 渲染界面

  4. componentDidMount() 挂载完成,在此进行异步操作

1. constructor()

  • 在构造函数里面初始化状态,这里不能调用 this.State()

  • 通过props设置的状态称之为派生状态

    • 父组件里面的props是不能直接更改的,解决方案就是设置派生状态

// 把父组件传递过来的属性数据传递给构造函数
constructor(props){
    super(props)
    this.state = {
        a:props.n // 通过props设置的状态称之为派生状态
    }
}

 

2. static getDetivedStateFromProps()

作用:通过父组件传递过来的属性设置派生状态

  • 什么时候会执行? new props setState forceUpdate

    • 当初始化的时候 会执行

    • 当父组件传递属性过来时 会执行

    • 当状态更新时 会执行

  • 在 return 里设置组件状态

  • 一定要 return ,如果无须设置组件状态可以返回 null

  • 两个参数

    • props 父组件通过设置属性传递过来的数据

    • state 组件的状态

static getDerivedStateFromProps(props,state){
    // 返回一个对象就是设置派生状态
    return {
      a:props.n
    }
}

3. render()

作用:写 html 结构代码

什么时候会执行: 初始化,new props,setState(),forceUpdata()。

  • new props: 父组件传过来的属性发生变化时。

  • setState(): 自身状态更新时。

  • forceUpdate(): 强制更新,即:强制执行一遍 render() 

render() {
    return (
        <div>
           // 组件页面结构
        </div>
    )
}

 

4. componentDidMount()

  • 什么时候执行?

    • 会在组件挂载后(插入 DOM 树中)立即调用。依赖于 DOM 节点的初始化应该放在这里。如需通过网络请求获取数据,此处是实例化请求的好地方。

  • 在这个函数里面执行异步操作

    • 因为在这里可以操作真实DOM

    • 异步操作的最终目的就是设置组件的状态setState({}),在这可以设置状态 this.setState({}) ,

      • constructor() 不能 调用 this.setState()

      • static getDetivedStateFromProps() 不能获取thi

     

    这个方法是比较适合添加订阅的地方。如果添加了订阅,请不要忘记在 componentWillUnmount() 里取消订阅。

    你可以在 componentDidMount()直接调用 setState()。它将触发额外渲染,但此渲染会发生在浏览器更新屏幕之前。如此保证了即使在 render() 两次调用的情况下,用户也不会看到中间状态。请谨慎使用该模式,因为它会导致性能问题。通常,你应该在 constructor() 中初始化 state。如果你的渲染依赖于 DOM 节点的大小或位置,比如实现 modals 和 tooltips 等情况下,你可以使用此方式处理

     

    componentDidMount(){
        axios.get("http://localhost:4000/list").then(res => {
            // 在回调中设置状态,回调函数就是异步完成后要执行的函数
            this.setState({
                list:res.data,
                name:‘‘
            })
        })
    }

     

注意:当组件的状态或者外部传入进来的属性发生改变的时候,组件的render函数会重新执行。

 

更新阶段

更新阶段周期函数的执行顺序为:

  1. static getDerivedStateFromProps(props,state)

  2. shouldComponentUpdate(nextProps,nextState)

  3. render()

  4. getSnapshotBeforeUpdate(prevProps, prevState)

  5. componentDidUpdate(prevProps, prevState, snapshot)

 

2.shouldComponentUpdate(nextProps, nextState)

作用: 当 props或state 发生变化时阻止组件的更新,即:阻止render()函数的执行。

意义:提高性能优化,减少不必要的 render() 执行行为。例如循环渲染子组件时。

阻止方法:返回 false。 只要返回false render() 函数就不会执行。

什么时候会触发:setState()、new props。 setState() 状态发生变化时,new props 子组件的属性发生变化时。不过当执行了 this.forceUpdate() 的时候即使是状态发生了变化它也不会被触发。

什么时候不触发:初始化的时候不会触发。forceUpdate()不会触发。

两个参数:nextProps, nextState。

  • nextProps:拿到更改后的属性值,在函数里面 this.props 拿到的是更改之前的属性

  • nextState:拿到自身状态发生改变后的状态,在函数里面 this.state 拿到的是更改之前的属性。

    注:如果改变状态时使用++更改数值状态,那么nextState.a 和 this.state.a 拿到的都是更新后的值。

    setState({
    a:++this.state.a
    })
  • 这两个参数存在的意义:可以通过判断前后是否相同来决定是否更新组件(执行一遍render())。

PureComponent

作用:继承纯组件可以自动对比前后状态的更改,如果状态或属性更改了才执行 render()。这样就可以高性能了,也不用去判断,内部都已经做了处理。

defalut class index extends PureComponent {}
  • 需要导入

    import {PureComponent} from ‘react‘
  • 纯组件里面不能再复写shouldComponentUpdate()

  • 内部进行的比较是浅比较,只能比较值和地址的变化,arr[1, 2] ==> arr[1, 2, 3] 会被认为没有发生变化

    • 解决不能比较数组元素发生变化的问题:给状态设置一个新的数组

      let newArr = [...this.state.arr, 99]
      // this.state.arr = [1,2,4,5]
      this.setState({
         arr:newArr
      })

React.memo()

无状态组件的性能优化,与PureComponent相似

export default React.memo(function One(props) {
    let {item, index, change, } = props
    console.log("render")
    return (
        <div>
            <label>{item.text}</label>
            <input type="checkbox" onChange={change.bind(this, index)} checked={item.flag}></input>
        </div>
    )
})

4. getSnapshopBeforeUpdate(prevProps, prevState)

  • 必须配合 componentDidUpdate() 使用

  • 什么时候触发:组件更新前(render 触发时 ==》 newprops、setState、forceUpdate )

  • 必须有返回值, 返回值不能是undefined。

  • 两个参数

    • prevProps: 属性更新前的属性(只要子组件被渲染一遍,就会更新属性,那么就会触发getSnapshopBeforeUpdate)

    • prevState: 状态更新前的状态

5. componentDidUpdate(prevProps, prevState, snapshot)

  • 什么时候触发:会在更新后会被立即调用。首次渲染不会执行此方法。如果 shouldComponentUpdate() 返回值为 false,则不会调用 componentDidUpdate()

  • 三个参数

    • prevProps: 组件更新前的属性

    • prevState: 组件更新前的状态

    • snapshot: getSnapshotBeforeUpdate() 的返回值。如果getSnapshotBeforeUpdate() 没有实现则返回 undefined.

 

当组件更新后,可以在此处对 DOM 进行操作。如果你对更新前后的 props 进行了比较,也可以选择在此处进行网络请求。(例如,当 props 未发生变化时,则不会执行网络请求)。

componentDidUpdate(prevProps) {
  // 典型用法(不要忘记比较 props):
  if (this.props.userID !== prevProps.userID) {
    this.fetchData(this.props.userID);
  }
}

你也可以在 componentDidUpdate()直接调用 setState(),但请注意它必须被包裹在一个条件语句里,正如上述的例子那样进行处理,否则会导致死循环。它还会导致额外的重新渲染,虽然用户不可见,但会影响组件性能。不要将 props “镜像”给 state,请考虑直接使用 props。 欲了解更多有关内容,请参阅为什么 props 复制给 state 会产生 bug

如果组件实现了 getSnapshotBeforeUpdate() 生命周期(不常用),则它的返回值将作为 componentDidUpdate() 的第三个参数 “snapshot” 参数传递。否则此参数将为 undefined。

 

 

 

卸载阶段

componentWillUnmount()

  • 什么时候执行?在这里能做什么?

      会在组件卸载及销毁之前直接调用。在此方法中执行必要的清理操作,例如,清除 timer,取消网络请求或清除在 componentDidMount() 中创建的订阅等。如果组件卸载了定时器确没有清除,那么定时器会一直执行。

  • 卸载组件的方法:

    ReactDOM.unmountComponentAtNode(document.getElementById(‘root‘)

// 设置定时器在挂载完毕这个组件生命周期函数里面设置
componentDidMount(){
    this.timer = setInterval(function(){
        console.log(123)
    },1000)
}
?
componentWillUnmount(){
    clearInterval(this.timer)
}
?
render() {
    return (
        <div>
           <button onClick={()=>    {ReactDOM.unmountComponentAtNode(document.getElementById(‘root‘))}}>卸载组件</button>
        </div>
    )
}

 

以上是关于React 组件的生命周期的主要内容,如果未能解决你的问题,请参考以下文章

react讲解(组件,生命周期以及受控组件)

react讲解(组件,生命周期以及受控组件)

react讲解(组件,生命周期以及受控组件)

react组件生命周期

react生命周期

react-组件生命周期