React组件生命周期

        React在17.0.1版本修改了组件的生命周期,新的生命周期和旧的生命周期改变不大,新的生命周期增加了两个新的钩子函数,有三个钩子函数过时了。 

一,旧生命周期

        组件的生命周期可以分为三个阶段:初始化阶段,更新阶段,卸载阶段。

        初始化阶段:依次触发。

                 constructor() --->

                 componentWillMount() --->

                 render() --->

                 componentDidMount()

        更新阶段:有三种更新的方式,不同的方式从不同的钩子函数依次向下触发函数。

                (1),第一种,也是更新阶段的整个阶段,由父组件重新render触发

                        父组件render --->

                        componentWillReceiveProps() --->

                        shouldComponentUpdate() --->

                        componentWillUpdate --->

                        render() --->

                        componentDidUpdate()

                (2),第二种,修改状态后触发(执行setState())

                        执行setState() ---> 

                        shouldComponentUpdate() --->

                        componentWillUpdate --->

                        render() --->

                        componentDidUpdate()

                (3),第三种,强制更新,执行forceUpdate()

                        执行forceUpdate() ---> 

                        componentWillUpdate --->

                        render() --->

                        componentDidUpdate()

        卸载阶段:由ReactDOM.unmountComponentAtNode()触发

                componentWillUnmount()

初始化阶段

<script type="text/babel">
        // 创建组件
        class Sum extends React.Component{

            constructor(props) {
                console.log('constructor')
                super(props)
                // 初始化状态
                this.state = {count: 0}
            }

            // 增加按钮
            add = () => {
                // 获取原状态
                const {count} =this.state
                // 更新状态
                this.setState({count: count+1})
            }

            // 组件将要挂载
            componentWillMount() {
                console.log('componentWillMount')
            }

            // 组件挂载完毕
            componentDidMount() {
                console.log('componentDidMount')
            }

            //render调用的时机:初始化渲染,状态更新之后
            render() {
                console.log('render')
                const {count} = this.state
                return(
                    <div>
                        <h2>{count}</h2>
                        <button onClick={this.add}>+1</button>
                    </div>
                )
            }
        }
        ReactDOM.render(<Sum />, document.getElementById('test'))
    </script>

         

        在初始化的阶段,常用的钩子函数是componentDidMount(),组件挂载完毕,例如,页面显示之后有一个计时的功能。

页面刚挂载好页面上的数字每隔一秒加1,定时器写到componentDidMount()中:

<script type="text/babel">
        // 创建组件
        class Sum extends React.Component{

            state = {count:0}

            unmount = () => {
                // 卸载组件
                ReactDOM.unmountComponentAtNode(document.getElementById('test'))
            }

            // 组件挂载完毕只调用一次
            componentDidMount() {
                // 设置一个定时器,每隔一秒加一
                this.timer = setInterval(()=>{
                    let {count} = this.state
                    count += 1
                    this.setState({count})
                },1000);
            }

            // 组件将要卸载的时候
            componentWillUnmount() {
                // 清空计时器
                clearInterval(this.timer)
            }

            //render调用的时机:初始化渲染,状态更新之后
            render() {
                return(
                    <div>
                        <h2>{this.state.count}</h2>
                        <button onClick={this.unmount}>卸载组件</button>
                    </div>
                )
            }
        }
        // 渲染组件到页面
        ReactDOM.render(<Sum/>, document.getElementById('test'))
    </script>

 更新阶段:

         第一种中提到了父组件,这种方法需要两个组件,一个子组件,一个父组件。

        首先创建两个组件,A组件,B组件,计算结果在A组件中进行,然后将计算好的结果传到B组件中,传输数据用props传,显示计算的结果在B组件中显示。A组件中将结果存到状态state中,更新状态必定会更新render,所以构成了由父组件重新render的条件。

<script type="text/babel">
        //A组件
        class Acomponent extends React.Component{
            state = {count: 0}
            add = () => {
                this.setState({count: this.state.count+1})
            }

            render() {
                return(
                    <div>
                        <div>A组件</div>
                        <button onClick={this.add}>+1</button> 
                        <Bcomponent count={this.state.count}/>   
                    </div>
                )
            }
        }
        
        //B组件
        class Bcomponent extends React.Component{
            componentWillReceiveProps() {
                console.log('componentWillReceiveProps')
            }

             // 控制组件更新 如果不写会有一个默认值为真
            // 写了必须有一个返回值
            shouldComponentUpdate() {
                console.log('shouldComponentUpdate')
                return true
            }

            // 组件将要更新
            componentWillUpdate() {
                console.log('componentWillUpdate')
            }

            // 组件更新完毕
            componentDidUpdate() {
                console.log('componentDidUpdate')
            }

            render() {
                console.log('render')
                return(
                    <div>B组件{this.props.count}</div>
                )
            }
        }
        // 渲染组件到页面
        ReactDOM.render(<Acomponent />, document.getElementById('test'))
    </script>

 

        只有第一种更新情况才会触发 componentWillReceiveProps() 钩子函数。

        shouldComponentUpdate()这个钩子函数的作用是可以控制组件的更新,返回一个布尔值,如果返回为true,会继续向下执行。

        如果返回为false,组件就不会更新,之后的钩子函数也不会执行。

         如果在代码中没有写shouldComponentUpdate()这个钩子函数,React会给一个默认值,默认值为true,如果代码中写了这个钩子函数,就必须写返回值。不写返回值会报错。

       第二种方法是更新状态,更新状态会重新加载组件。

        后面的内容不多,所以修改状态更新,强制更新和卸载组件的代码就写到一个代码里了。

<script type="text/babel">
        // 创建组件
        class Sum extends React.Component{

            state = {count:0}

            // 按钮的回调
            add = () => {
                // 获取原状态
                const {count} =this.state
                // 更新状态
                this.setState({count: count+1})
            }

            // 卸载组件
            unmount = () => {
                ReactDOM.unmountComponentAtNode(document.getElementById('test'))
            }

            // 强制更新
            force = () => {
                this.forceUpdate();
            }

            // 组件将要卸载
            componentWillUnmount() {
                console.log('componentWillUnmount')
            }

            // 控制组件更新 如果不写会有一个默认值为真
            // 写了必须有一个返回值
            shouldComponentUpdate() {
                console.log('shouldComponentUpdate')
                return true
            }

            // 组件将要更新
            componentWillUpdate() {
                console.log('componentWillUpdate')
            }

            // 组件更新完毕
            componentDidUpdate() {
                console.log('componentDidUpdate')
            }

            //render调用的时机:初始化渲染,状态更新之后
            render() {
                console.log('render')
                const {count} = this.state
                return(
                    <div>
                        <h2>{count}</h2>
                        <button onClick={this.add}>+1</button>
                        <button onClick={this.unmount}>卸载组件</button>
                        <button onClick={this.force}>不改变状态,强制更新</button>
                    </div>
                )
            }
        }
        // 渲染组件到页面
        ReactDOM.render(<Sum />, document.getElementById('test'))
    </script>

        

         第一个+1的按钮就是更新状态的按钮,将显示的数字存到状态中。依次会触发

 

        可以通过控制 shouldComponentUpdate() 的返回值来控制是否更新组件。

        第三种方法是强制更新组件,通过调用forceUpdate()函数。

        因为是强制更新,必定更新,所以不用通过shouldComponentUpdate() 来控制。

        在写代码的过程中,可以更新状态不更新组件,可以不更新状态强制更新组件,根据自己需要的场景选择。

卸载阶段

        卸载阶段的话只有一个钩子函数,componentWillUnmount() 组件将要卸载,由React.unmountComponentAtNode() 触发卸载组件。

        在这个阶段会做一些收尾性的工作,例如,前面的那个定时器的例子,在卸载时要把定时器在这个钩子函数中清除,不然会报错。

二,新生命周期

        在了解新生命周期之前,我们要知道React为什么要修改生命周期,官网中的内容:

         上面三个生命周期方法经常被误解和滥用,React团队要实现异步渲染,而在异步渲染中,它们潜在的误用问题可能更大,在React未来版本中可能出现bug。

        就是说React团队在为之后React版本的更新提前铺路,把可能出现的问题提前解决一下。

        引入了两个新的生命周期,静态的 getDerivedStateFromProps和 getSnapshotBeforeUpdate.

        getDerivedStateFromProps() 强调它是静态的,所以在使用时,前面要加上static。

<script type="text/babel">
        // 创建组件
        class Count extends React.Component{

            state = {count: 0 }

            // 按钮的回调
            add = () => {
                // 获取原状态
                const {count} =this.state
                // 更新状态
                this.setState({count: count+1})
            }

            // 从props得到一个派生的state
            // 如果state的值在任何时候取决于props,可以使用
            static getDerivedStateFromProps(props, state) {
                console.log('getDeriveStateFromProps', props, state)
                return null
                // return props
            }

            getSnapshotBeforeUpdate(prevProps, prevState) {
                console.log('getSnapshotBeforeUpdate', prevProps, prevState)
                return 'React'
            }

            // 组件更新完毕
            componentDidUpdate(prevProps, prevState, snapshotValue) {
                console.log('componentDidUpdate', prevProps, prevState, snapshotValue)
            }

            //render调用的时机:初始化渲染,状态更新之后
            render() {
                console.log('render')
                const {count} = this.state
                return(
                    <div>
                        <h2>{count}</h2>
                        <button onClick={this.add}>+1</button>
                        {/*<button onClick={this.death}>卸载组件</button>
                    <button onClick={this.force}>不改变状态,强制更新</button>*/}
                    </div>
                )
            }
        }
        ReactDOM.render(<Count count={2}/>, document.getElementById('test'))
    </script>

        它可以返回一个对象来更新state,或者返回null来表示新的props,不需要任何state的更新。但使用getDerivedStateFromProps方法会增加组件的复杂性,经常会导致bug,所以使用到这个钩子函数的情况极其少,能不用就不用。

        另一个新的生命周期是getSnapshotBeforeUpdate,这个生命周期的返回值将作为第三个参数传递给componentDidUpdate.

        图是上面代码的运行结果。

        上面两行是组件初始化时触发的,下面四行时组件更新时触发的,getDerivedStateFromProps可以拿到当前组件的props和state的值,一直都是当前的值,而getSnapshotBeforeUpdate它拿的是之前的值,是组件还没有更新时候的值。

        当组件更新完毕后,是更新之后的组件,更新之前的组件中的信息就没有了,之前组件的信息可以由getSnapshotBeforeUpdate返回并传给componentDidUpdate,能存储之前组件的信息。什么都能存储,例如,保留滚动位置,组件的大小数据等。在一些情况下还是非常有用的。

总结

        不管是旧生命周期还是新生命周期,常用的钩子函数有三个

        componentDidMount(),初始化的工作;

        componentDidUpdate(),更新的工作;

        componentWillUnmount(),收尾的工作。

        这三个钩子函数在旧生命周期或新生命周期中都没有改变。

三,函数式组件中使用生命周期钩子函数

        Hook是React16.8.0版本增加的新特性,新语法,可以在函数组件中使用state,refs以及其他的React特性(生命周期钩子函数)。

Effect Hook

        Effect Hook 可以让你在函数组件中执行副作用操作(用于模拟类组件中的生命周期钩子)。

import React from "react"
import  ReactDOM  from "react-dom"

function Component() {

    const [count, setCount] = React.useState(0)

    function add() {
        setCount((count)=>{return count+1})
    }

    function unmount() {
        ReactDOM.unmountComponentAtNode(document.getElementById('root'))
    }
    

    //相当于componentDidMount()
    // React.useEffect(()=>{
    //     console.log('初始化')
    // },[])  //括号中什么都不加,只在页面刚挂载时调用


    //相当于componentDidUpdate()
    // React.useEffect(()=>{
    //     console.log('初始化并更新')
    // },[count]) //括号中加入状态,当状态改变时更新


    //相当于componentWillUnmount() 
    React.useEffect(()=>{
        console.log('初始化')
        return ()=>{
            console.log('卸载组件')
        }
    },[])


    return (
        <div>
            <h2>{count}</h2>
            <button onClick={add}>+1</button>
            <button onClick={unmount}>卸载</button>
        </div>
    )
}

export default Component

(1)语法和说明: 

        useEffect(() => { 

          // 在此可以执行任何带副作用操作

          return () => { // 在组件卸载前执行

            // 在此做一些收尾工作, 比如清除定时器/取消订阅等

          }

        }, [stateValue]) // 如果指定的是[], 回调函数只会在第一次render()后执行

(2)可以把 useEffect Hook 看做如下三个函数的组合

        componentDidMount()

        componentDidUpdate()

        componentWillUnmount() 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值