React-生命周期

536 阅读8分钟

生命周期函数(钩子函数):

描述一个组件或者从程序从创建到销毁的过程,我们可以在过程中间基于钩子函数完成一些自己的操作.(如,首次渲染时做什么,更新操作做什么等等).

[基本流程]

constructor 创建一个组件
componentWillMount 第一次渲染之前
render 第一次渲染
componentDidMount 第一次渲染之后

[更新流程]

componentWillReceiveProps:父组件把传递给子组件的属性发生改变后触发的钩子函数(子组件先走这个钩子获取最新的状态)
shouldComponentUpdate 是否允许组件重新渲染(允许则执行后面函数,不允许直接结束即可)
componentWillUpdate 重新渲染之前
render 第二次及以后重新渲染
componentDidUpdate 重新渲染之后

[卸载]

卸载:原有渲染的内容是不消失,只不过以后不能基于数据改变视图了 componentWillUnmount: 卸载组件之前(一般不用)(把组件中的定时器等置空)

测试代码

class A extends React.Component{
    //=>这个是第一次执行的,执行完成后(给属性设置默认值后才向下执行)
    static defaultProps = {};
    
    constructor(){
        super()
        console.log('子组件----01constructor')
        this.state = {
            n: 1
        }
    }
    
    componentWillMount(){
    	console.log('子组件----02componentWillMount-第一次渲染之前',this.refs.HH)  //undefined

    	// =》在WILL_MOUNT中,如果直接的SET-STATE修改数据,会把状态信息改变,然后RENDER和DID_MOUNT;但是如果SET-STATE是放到一个异步操作中完成(例如:定时器或者从服务器数据),也先会执行RENDER和DID,然后在执行这个异步操作修改状态,紧接着走修改的流程(这样和放到DID_MOUNT中也没有啥区别的),所以我们一般把数据请求放到DID处理
    	// =》真实项目中的数据绑定,一般第一次组件渲染,我们都是绑定的默认数据,第二次才是绑定的从服务器获取数据,(有些需求我们需要是否存在判断显示隐藏等)
    	setInterval(()=>{
    		this.setState({
    			n: this.state.n+1
    		})
    	},5000)
    }
    componentDidMount(){
        console.log('子组件----03omponentDidMount-第一次渲染之后',this.refs.HH) // <div>1</div>
        /*
         * 真实项目中在这个阶段一般做如下处理:
         *  1.控制状态信息更改的操作
         *  2.从服务器获取数据,然后修改状态,完成数据绑定
         。。。
         */
        // setInterval(()=>{
        // 	this.setState({
        // 		n: this.state.n+1
        // 	})
        // },5000)
        
    }
    render(){
        console.log("子组件----04 RENDER")
        return <div ref='HH'>{ this.state.n }</div>
    }
    
    componentWillReceiveProps(nextProps,nextState){
        // 组件属性改变
        console.log("子组件--父组件属性改变",this.props.n,nextProps,nextState)
    }
    shouldComponentUpdate(nextProps,nextState){
        
        console.log("子组件---05shouldComponentUpdate是否允许更新,函数返回TRUE就是允许,返回FALSE就是不允许",nextProps,nextState);
        
        /*
         在这个钩子函数中,我们获取的STATE不是最新修改的,而是上一次的sSTATE值
         * 例如: 第一次加载完成:5000MS后,我们基于SET
         -STATE把N修改为2,但是此处获取的还是1呢
         * 但是这个方法有两个参数:
         * nextProps:最新修改属性信息
         * nextState 最新修改的状态信息
         */
        if(nextState.n>3){
            return false
        }else{
            return true
        }
    }
    componentWillUpdate(nextProps,nextState){
        // 这里获取的状态是更新之前的,(和SHOULD相同也有两个参数存储最新的信息)
        console.log("子组件---06-componentWillUpdate:组件更新之前",this.state.n,nextProps,nextState)
    }
    componentDidUpdate(){
        // 这里获取的状态是更新之后的
        console.log("子组件---08-componentDidUpdate:组件更新之后",this.state.n)
    }
    
}

class B extends React.Component{
    constructor(){
        super()
        this.state = {
            n: 1
        }
        console.log('父组件----01 constructor')
    }
    componentWillMount(){
        // setTimeout(()=>{
        //     this.setState({
        //         n:2
        //     })
        // },3000)
        console.log('父组件----02 componentWillMount')
    }
    
    componentDidMount(){
        setTimeout(()=>{
            this.setState({
                n:2
            })
        },3000)
        console.log('父组件----03 componentDidMount')
    }
    
    shouldComponentUpdate(nextProps,nextState){
        console.log("父组件---05-shouldComponentUpdate:是否允许更新,函数返回TRUE就是允许,返回FALSE就是不允许",nextProps,nextState)
        return true;
    }
    componentWillUpdate(nextProps,nextState){
        // 这里获取的状态是更新之前的,(和SHOULD相同也有两个参数存储最新的信息)
        console.log("父组件---06-componentWillUpdate:组件更新之前",this.state.n,nextProps,nextState)
    }
    
    componentDidUpdate(){
        // 这里获取的状态是更新之后的
        console.log("父组件---08-componentDidUpdate:组件更新之后",this.state.n)
    }
    render(){
        console.log('父组件----04 render')
        return <div>
            {/* 把父组件的状态作为属性传递给子组件 */}
            <A n = { this.state.n } />
        </div>
    }
}

旧的生命周期流程图

React最新的生命周期

componentWillMount has been renamed, and is not recommended for use.  ==> componentWillMount已重命名,建议不要使用

componentWillReceiveProps has been renamed, and is not recommended for use. ==>  details.componentWillReceiveProps已重命名,建议不要使用

...

16.3版本的生命周期

16.4版本的生命周期

react16.3以后

新增了getDerivedStateFromProps,getSnapshotBeforeUpdate

删除了 componentWillMount,componentWillUpdate,componentWillReceiveProps

getDerivedStateFromProps(nextProps, prevState)

getDerivedStateFromProps() ==> 从属性获取派生状态(语义化) ==> 将传入的props映射到state中.

getDerivedStateFromProps是一个静态函数,也就是这个函数不能通过this访问到class的属性,也并不推荐直接访问属性。而是应该通过参数提供的nextProps以及prevState来进行判断,根据新传入的props来映射到state。

注意

如果props传入的内容不需要影响到你的state,那么就需要返回一个null,这个返回值是必须的,所以尽量将其写到函数的末尾;没有返回值会报警告

static getDerivedStateFromProps(nextProps, prevState) {
    console.log('子组件-----getDerivedStateFromProps',nextProps,prevState);
    const { n } = nextProps;
    
    // getDerivedStateFromProps是一个静态函数,也就是这个函数不能通过this访问到class的属性,也并不推荐直接访问属性。而是应该通过参数提供的nextProps以及prevState来进行判断,根据新传入的props来映射到state。
    if(n !== prevState.n){
        return {
            n       // 直接映射到state中
        };
    }
    return null;
}

应用场景详细看这里

getSnapshotBeforeUpdate(prevProps, prevState)

getSnapshotBeforeUpdate(prevProps, prevState) => 在render之后调用,state已更新.

应用: 获取render之前的dom状态

在最近的更改被提交到DOM元素前,使得组件可以在更改之前获得当前值,此生命周期返回的任意值都会传给componentDidUpdate()。

注意

getSnapshotBeforeUpdate()必须有返回值,没有返回值会报警告

getSnapshotBeforeUpdate(prevProps,prevState){
    console.log("子组件---getSnapshotBeforeUpdate",prevProps,prevState);
    return 111
}
// componentDidUpdate第三个参数接受getSnapshotBeforeUpdate()钩子函数的返回值
componentDidUpdate(prevProps, prevState,perScrollHeight){
    console.log(prevProps,prevState,perScrollHeight)
    // 这里获取的状态是更新之后的
    console.log("子组件---08-componentDidUpdate:组件更新之后",this.state.n)
}

测试代码

class A extends React.Component{
    //=>这个是第一次执行的,执行完成后(给属性设置默认值后才向下执行)
    static defaultProps = {};
    
    constructor(){
        super()
        console.log('子组件----01constructor')
        this.state = {
            n: 1
        }
    }
    
    // getDerivedStateFromProps是一个静态函数,也就是这个函数不能通过this访问到class的属性,也并不推荐直接访问属性。而是应该通过参数提供的nextProps以及prevState来进行判断,根据新传入的props来映射到state。
    static getDerivedStateFromProps(nextProps, prevState) {
        console.log('子组件-----getDerivedStateFromProps',nextProps,prevState);
    
        const { n } = nextProps;
        if(n !== prevState.n){
            return {
                n       // 直接映射到state中
            };
        }
        return null;    // 如果返回的null,不会把props中的数据映射到state中
    }
    
    
    componentDidMount(){
        console.log()
        console.log('子组件----03omponentDidMount-第一次渲染之后',this.refs.HH) // <div>1</div>
    }
    
    shouldComponentUpdate(nextProps,nextState){
        console.log("子组件---05shouldComponentUpdate是否允许更新,函数返回TRUE就是允许,返回FALSE就是不允许",nextProps,nextState);
        if(nextState.n>3){
            return false
        }else{
            return true
        }
    }
    
    // 获取上一次的值,做一次事情,例如官网给的例子,, 滚动条
    // getSnapshotBeforeUpdate() 在最近一次渲染输出(提交到 DOM 节点)之前调用。它使得组件能在发生更改之前从 DOM 中捕获一些信息(例如,滚动位置)。此生命周期的任何返回值将作为参数传递给 componentDidUpdate()。
    getSnapshotBeforeUpdate(prevProps,prevState){
        console.log("子组件---getSnapshotBeforeUpdate",prevProps,prevState);
        return 111
    }
    
    
    // componentWillUpdate(nextProps,nextState){
    //     // 这里获取的状态是更新之前的,(和SHOULD相同也有两个参数存储最新的信息)
    //     console.log("子组件---06-componentWillUpdate:组件更新之前",this.state.n,nextProps,nextState)
    // }
    
    componentDidUpdate(prevProps, prevState,perScrollHeight){
        console.log(prevProps,prevState,perScrollHeight)
        // 这里获取的状态是更新之后的
        console.log("子组件---08-componentDidUpdate:组件更新之后",this.state.n)
    }
    
    
    render(){
        console.log("子组件----04 RENDER")
        return <div ref='HH'>{ this.state.n }</div>
    }
}

class B extends React.Component{
    constructor(){
        super()
        this.state = {
            n: 1
        }
        console.log('父组件----01 constructor')
    }
    // componentWillMount(){
    //     console.log('父组件----02 componentWillMount')
    // }
    
    static getDerivedStateFromProps(nextProps, prevState) {
        // // 组件每次被rerender的时候,包括在组件构建之后(虚拟dom之后,实际dom挂载之前),每次获取新的props或state之后;;每次接收新的props之后都会返回一个对象作为新的state,返回null则说明不需要更新state
        console.log('父组件-----getDerivedStateFromProps')
        return null;
    }
    
    
    componentDidMount(){
        setTimeout(()=>{
            this.setState({
                n:2
            })
        },3000)
        console.log('父组件----03 componentDidMount')
    }
    
    shouldComponentUpdate(nextProps,nextState){
        console.log("父组件---05-shouldComponentUpdate:是否允许更新,函数返回TRUE就是允许,返回FALSE就是不允许",nextProps,nextState)
        return true;
    }
    
    
    getSnapshotBeforeUpdate(nextProps,nextState){
        console.log("父组件--getSnapshotBeforeUpdate",nextProps,nextState)
        return null;
    }
    
    // componentWillUpdate(nextProps,nextState){
    //     // 这里获取的状态是更新之前的,(和SHOULD相同也有两个参数存储最新的信息)
    //     console.log("父组件---06-componentWillUpdate:组件更新之前",this.state.n,nextProps,nextState)
    // }
    
    componentDidUpdate(){
        // 这里获取的状态是更新之后的
        console.log("父组件---08-componentDidUpdate:组件更新之后",this.state.n)
    }
    
    
    render(){
        console.log('父组件----04 render')
        return <div>
            {/* 把父组件的状态作为属性传递给子组件 */}
            <A n = { this.state.n } />
        </div>
    }
}