react的生命周期

生命周期是我们在react中碰到的第二个新概念。如同人有生老病死,自然界有日月更替,每个组件也有被创建、更新和删除,如同有生命的机体一样。

react严格定义了组件的生命周期,生命周期可能会经历三个过程:

  • 装载过程(mount):React.js 将组件渲染,并且构造 DOM 元素然后塞入页面的过程

  • 更新过程(update):当组件被重新渲染的过程

  • 卸载过程(unmount):组件从DOM中删除的过程

装载过程

装载过程依次调用的函数如下:

constructor:初始化state,绑定成员函数的this环境

//通过React.createClass这两个生命周期才会用到,getInitialState被constructor中的this.state代替了,getDefaultProps被static defaultProps代替了
React.createClass({
  getInitialState:初始化state
  getDefaultProps:提供默认的props
})

componentWillMount:在render之前调用,一般不再这个函数里里面做什么事,因为这时候dom还没有渲染出来,所有在这里可以做的事都可以在componentDidMount里面执行。可以在服务器端执行,也可以在浏览器端执行。在这里面执行setState,组件会更新state,但组件只渲染一次,因此这是无意义的执行,初始化的state都可以放到constructor里面去做

render:render函数并不做实际的渲染,它只是返回一个jsx,最终由react来操作渲染过程,它可以返回null或者false,等于告诉react这次不要渲染任何dom元素。render应该是一个纯函数,完全根据props、state来决定要返回的东西。render里面不能使用setState,因为一个纯函数不应该引起状态的改变

componentDidMount:在组件render之后执行,所有动画的启动,ajax请求、定时器皆可以放到这里面执行,这个函数也是用的最多的函数之一,这里面执行setState当然可以会再次更新组建。这个函数与render函数有一个不大不小的坑,可能有人认为render执行后立即调用componentDidMount,实际上却不是这样的。每一次render都是递归执行的,而之所以react这么设计是因为每个render函数返回的都是jsx表示的对象,然后由react根据返回对象来决定如何渲染,而react肯定是要把所有组件的结果综合起来,才能知道如何产生对应的DOM修改。所以只有当react调用所有组件的render之后才可能完成装载继而执行componentDidMount,所以componentDidMount才连在一起被调用。可以在浏览器端执行,不能在服务器端执行

示例

// ControlPanel
import React,{Component} from 'react'
import Counter from './Counter'

const style = {
    margin:'20px'
}
export default class ControlPanel extends Component{
    render(){
        console.log('enter ControlPanel render');
        return(
            <div style={style}>
                <Counter caption="First" />
                <Counter caption="Second" initValue={10} />
                <Counter caption="Third" initValue={20} />
                <button onClick={ () => this.forceUpdate() }>
                    Click me to re-render!
                </button>
            </div>
        )
    }
}
//Counter 
import React,{Component} from 'react'
import PropTypes from 'prop-types'

const buttonStyle = {
    margin: '10px'
}

export default class Counter extends Component{
    static propTypes = {
        caption: PropTypes.string.isRequired,
        initValue: PropTypes.number
    }

    static defaultProps = {
        initValue: 0
    }
    constructor(props){
        super(props)
        this.onClickIncrementButton = this.onClickIncrementButton.bind(this)
        this.onClickDecrementButton = this.onClickDecrementButton.bind(this)

        this.state = {
            count:props.initValue,
        }
        console.log('enter constructor: ' + props.caption);
    }

    componentWillMount() {
        console.log('enter componentWillMount ' + this.props.caption);
    }

    componentDidMount() {
        console.log('enter componentDidMount ' + this.props.caption);
    }

    componentWillReceiveProps(nextProps) {
        console.log('enter componentWillReceiveProps ' + this.props.caption)
    }

    shouldComponentUpdate(nextProps, nextState) {
        console.log('enter shouldComponentUpdate ' + this.props.caption)
        return (nextProps.caption !== this.props.caption) ||
                (nextState.count !== this.state.count);
        //return true
       
      }


    componentWillUpdate(){
        console.log('enter componentWillUpdate ' + this.props.caption)
    }

    componentDidUpdate(){
        console.log('enter componentDidUpdate ' + this.props.caption)
    }

    onClickIncrementButton() {
        this.setState({count: this.state.count + 1});
    }
    
    onClickDecrementButton() {
        this.setState({count: this.state.count - 1});
    }

    render(){
        console.log('enter render ' + this.props.caption);
        return(
            <div>
                <button style={buttonStyle} onClick={this.onClickIncrementButton}>+</button>
                <button style={buttonStyle} onClick={this.onClickDecrementButton}>-</button>
                <span>{this.props.caption} count: {this.state.count}</span>
            </div>
        )
    }
}


  
//index.js
import React from 'react';
import ReactDOM from 'react-dom';
import ControlPanel from './ControlPanel';

ReactDOM.render(<ControlPanel />, document.getElementById('root'));

结果:

enter ControlPanel render (父组件)

enter constructor first
enter componentWillMount first
enter render first

enter constructor second
enter componentWillMount second
enter render second
 
enter constructor third
enter componentWillMount third
enter render third

enter componentDidMount first
enter componentDidMount second
enter componentDidMount third

更新过程

componentWillReceiveProps:按字面意思是组件是否接受props,但是实际上只要父组件render函数被调用,在render函数里面的子组件就会经历更新过程,而不管父组件传给子组件的props有没有改变,都会触发子组件的componentWillProps,上面那个示例只要我点击re-render那个按钮强制更新父组件,明明传给子组件的props没有更新,但是控制台却打印了enter componentWillReceiveProps 。通过this.setState触发的更新过程不会调用这个函数,这是因为这个函数适合根据新的props值(也就是参数nextProps)来计算要不要更新内部状态state,而更新内部状态的方法就是setState,如果setState的调用导致componentWillReceiveProps再一次被调用,就会陷入一个死循环。虽然this.setState触发的更新过程不会调用这个函数,但是可以在这个函数中执行this.setState,虽然没什么用。

shouldComponentUpdate:render函数决定了该渲染什么,而shouldComponentUpdate决定了一个组件什么时候不需要渲染。而它们两个也是所有函数中唯二需要返回结果的函数,render的返回结果用于构造dom对象,而shouldComponentUpdate函数需要一个返回值,告诉react这个组件这次更新过程是否要继续。在更新过程中,react首先调用shouldComponentUpdate,若它返回true则继续更新,接下来调用render函数,若它返回false,就立即停止更新过程。它能够大大提高react的性能,如果确定特定组件在分析后很慢,则可以将其更改为从React.PureComponent继承,React.PureComponent实现了与浅层prop和state比较的shouldComponentUpdate()。如果你确信你想用手写它,你可以将this.props与nextProps和this.state与nextState进行比较,并返回false来告诉React更新可以被跳过。不能在这里调用setState,否则会造成循环调用。

componentWillUpdate:在更新之前做准备工作,如果我们想根据新的props值来计算要不要更新内部状态state,可以在componentWillReceiveProps 这个函数中来执行。同shouldComponentUpdate,不能在这里直接执行setState,会造成死循环,不能在这里执行直接setState,会造成死循环,但是可以设置一个边界条件,此时就可以setState了。

render:重新渲染

componentDidUpdate:可以在这里操纵dom以及进行网络请求,与
componentDidUpdate不同的是componentDidMount既可以在浏览器端执行又可以在服务器端执行。但是在服务器端只需产出html字符串,所以正常情况下也不会调用componentDidUpdate。值得一提的是由于render函数是递归调用,所以组件更新跟组件装载一样,我们的示例组件会先自己更新到render,最后几个componentDidUpdate依次调用。这个方法可以调用setState。

此时先在shouldComponentUpdate返回true才能看到效果,然后点击re-render按钮

enter ControlPanel render

enter componentWillReceiveProps First
enter shouldComponentUpdate First
enter componentWillUpdate First
enter render First

enter componentWillReceiveProps Second
enter shouldComponentUpdate Second
enter componentWillUpdate Second
enter render Second

enter componentWillReceiveProps Third
enter shouldComponentUpdate Third
enter componentWillUpdate Third
enter render Third

enter componentDidUpdate First
enter componentDidUpdate Second
enter componentDidUpdate Third

卸载过程

componentwillUnmout:组件卸载的时候调用,这个函数适合清理性的工作,比如清除定时器、清除在componentDidMount创造的dom元素。

以下是从网上找来的图,引用于深入react技术栈

生命周期整体流程图
生命周期全局图
生命周期执行顺序图
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,198评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,663评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,985评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,673评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,994评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,399评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,717评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,407评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,112评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,371评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,891评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,255评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,881评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,010评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,764评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,412评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,299评论 2 260

推荐阅读更多精彩内容

  • 即使最近还是有点拖延,可还是再看一些关于React的东西。看到React组件的生命周期的时候,让我想到了Vue同样...
    白霁阅读 582评论 0 9
  • 实例化 首次实例化 getDefaultProps getInitialState componentWillMo...
    奔跑在春风里阅读 285评论 0 0
  • 3. JSX JSX是对JavaScript语言的一个扩展语法, 用于生产React“元素”,建议在描述UI的时候...
    pixels阅读 2,757评论 0 24
  • 挂载阶段的组件生命周期 我们将reactjs组件渲染并构造DOM元素然后塞入页面的过程称为组件的挂载, 我们知道在...
    秋枫残红阅读 443评论 0 1
  • React 组件的一生,是光荣的一生,是革命的一生,在它的一生中会经历这样几个阶段: 装载阶段 更新阶段 销毁阶段...
    柏丘君阅读 837评论 0 0