6) React Native 组件生命周期(ES6)

在React Native中使用组件来封装界面模块时,整个界面就是一个大的组件,开发过程就是不断优化和拆分界面组件、构造整个组件树的过程。
所以学习理解组件的生命周期显得尤为重要!

一、组件的属性(props)和状态(state)

1. 属性(props)

它是组件的不可变属性(组件自己不可以自己修改props)。
组件自身定义了一组props作为对外提供的接口,展示一个组件时只需要指定props作为节点的属性。
一般组件很少需要对外公开方法(例外:工具类的静态方法等),唯一的交互途径就是props。所以说它也是父组件与子组件通信的桥梁
组件自己不可以自己修改props(即:props可认为是只读的),只可由其他组件调用它时在外部修改。

2. 状态(state)

它是组件的内部状态属性,主要用来存储组件自身需要的数据。
除了初始化时可能由props来决定,之后就完全由组件自身去维护。
组件中由系统定义了setState方法,每次调用setState时都会更新组件的状态,触发render方法重新渲染界面
需要注意的是render方法是被异步调用的,这可以保证同步的多个setState方法只会触发一次render,这样做是有利于提高性能的。

二、组件的生命周期

对于自定义组件,除了必须实现的render方法,还有一些其他的可选方法可被调用。这些方法会在组件的不同时期之行,所以也可以说这些方法是组件的生命周期方法。
对于组件的生命周期来说一般分为四个阶段,分别为:
**创建阶段、实例化阶段、运行(更新)阶段、销毁阶段。 **

1. 创建阶段

该阶段主要发生在创建组件类的时候,在这个阶段中会初始化组件的属性类型和默认属性。

defaultProps / getDefaultProps()

这里会初始化一些默认的属性,通常会将固定的内容放在这个过程中进行初始化和赋值,一个控件可以利用this.props获取在这里初始化它的属性,由于组件初始化后,再次使用该组件不会调用getDefaultProps函数,所以组件自己不可以自己修改props(即:props可认为是只读的),只可由其他组件调用它时在外部修改。

在ES5里,属性类型和默认属性分别通过propTypes成员和getDefaultProps方法来实现。

//ES5
getDefaultProps: function() {
        return {
            autoPlay: false,
            maxLoops: 10,
        };
},
propTypes: {
        autoPlay: React.PropTypes.bool.isRequired,
        maxLoops: React.PropTypes.number.isRequired,
        posterFrameSrc: React.PropTypes.string.isRequired,
        videoSrc: React.PropTypes.string.isRequired,
},

在ES6里,可以统一使用static成员来实现.

//ES6
static defaultProps = {
        autoPlay: false,
        maxLoops: 10,
};  // 注意这里有分号
static propTypes = {
        autoPlay: React.PropTypes.bool.isRequired,
        maxLoops: React.PropTypes.number.isRequired,
        posterFrameSrc: React.PropTypes.string.isRequired,
        videoSrc: React.PropTypes.string.isRequired,
};  // 注意这里有分号

2. 实例化阶段

该阶段主要发生在组件类被调用(实例化)的时候。
组件类被实例化的时候,触发一系列流程:

1) constructor(props) / getInitialState()

这里是对控件的一些状态进行初始化,由于该函数不同于getDefaultProps,在以后的过程中,会再次调用,所以可以将控制控件的状态的一些变量放在这里初始化,如控件上显示的文字,可以通过this.state来获取值,通过this.setState来修改state值。

在ES5里,通过getInitialState对状态进行初始化

getInitialState: function() {
        return {
            loopsRemaining: this.props.maxLoops,
        };
},

在ES6里,通过constructor(构造器)对状态进行初始化

constructor(props){
        super(props);
        this.state = {
            loopsRemaining: this.props.maxLoops,
        };
}
2) componentWillMount()

准备加载组件。
这个调用时机是在组件创建,并初始化了状态之后,在第一次绘制 render() 之前。可以在这里做一些业务初始化操作,也可以设置组件状态。这个函数在整个生命周期中只被调用一次。
如果在这个函数里面调用setState,本次的render函数可以看到更新后的state,并且只渲染一次。

3) render()

render是一个组件必须有的方法,形式为一个函数,渲染界面,并返回JSX或其他组件来构成DOM,和Android的XML布局、WPF的XAML布局类似,只能返回一个顶级元素

4) componentDidUpdate()

调用了render方法后,组件加载成功并被成功渲染出来以后所执行的hook函数,一般会将网络请求等加载数据的操作,放在这个函数里进行,来保证不会出现UI上的错误

3. 运行(更新)阶段

该阶段主要发生在用户操作之后,或者父组件有更新的时候,此时会根据用户的操作行为,进行相应的界面结构调整。
触发的流程如下:

1) componentWillReceiveProps(nextProps)

当组件接收到新的props时,会触发该函数。在该函数中,通常可以调用setState()来完成对state的修改
输入参数 nextProps 是即将被设置的属性,旧的属性还是可以通过 this.props 来获取。在这个回调函数里面,你可以根据属性的变化,通过调用 this.setState() 来更新你的组件状态,这里调用更新状态是安全的,并不会触发额外的 render() 调用。如下:

componentWillReceiveProps: function(nextProps) {  
  this.setState({
    likesIncreasing: nextProps.likeCount > this.props.likeCount
  });
}
2) shouldComponentUpdate(nextProps, nextState)

返回布尔值(决定是否需要更新组件)

输入参数 nextProps 和上面的 componentWillReceiveProps 函数一样,nextState 表示组件即将更新的状态值。这个函数的返回值决定是否需要更新组件,如果 true 表示需要更新,继续走后面的更新流程。否者,则不更新,直接进入等待状态。
默认情况下,这个函数永远返回 true 用来保证数据变化的时候 UI 能够同步更新。在大型项目中,你可以自己重载这个函数,通过检查变化前后属性和状态,来决定 UI 是否需要更新,能有效提高应用性能。

3) componentWillUpdate(nextProps, nextState)

shouldComponentUpdate返回true或者调用forceUpdate之后,就会开始准更新组件,并调用 componentWillUpdate()。
输入参数与 shouldComponentUpdate 一样,在这个回调中,可以做一些在更新界面之前要做的事情。需要特别注意的是,在这个函数里面,你就不能使用 this.setState 来修改状态。这个函数调用之后,就会把 nextProps 和 nextState 分别设置到 this.props 和 this.state 中。紧接着这个函数,就会调用 render() 来更新界面了。

4) render()

再确定需要更新组件时,调用render,根据diff算法,渲染界面,生成需要更新的虚拟DOM数据。

5) componentDidUpdate()

虚拟DOM同步到DOM中后,执行该方法,可以在这个方法中做DOM操作。
除了首次render之后调用componentDidMount,其它render结束之后都是调用componentDidUpdate。

componentWillMount、componentDidMount和componentWillUpdate、componentDidUpdate可以对应起来。区别在于,前者只有在挂载的时候会被调用;而后者在以后的每次更新渲染之后都会被调用。
ps:绝对不要在componentWillUpdate和componentDidUpdate中调用this.setState方法,否则将导致无限循环调用。

4. 销毁阶段

该阶段主要发生组件销亡的时候,触发componentWillUnmount。当组件需要从DOM中移除的时候,通常需要做一些取消事件绑定,移除虚拟DOM中对应的组件数据结构,销毁一些无效的定时器等工作,都可以在这个方法中处理。

componentWillUnmount()

当组件要被从界面上移除的时候,就会调用 componentWillUnmount。
在这个函数中,可以做一些组件相关的清理工作,例如取消计时器、网络请求等

三、组件更新的方式(更新阶段详细)

本来是没有想要要详细写这部分内容的,不过看到另一篇文章,写得好好,就也放进来详细讲下,感谢原文作者
参考自:http://www.jianshu.com/p/4784216b8194 里的更新方式部分

更新组件(重新渲染界面)的方式有以下四种

  1. 首次渲染Initial Render,即首次加载组件
  2. 调用this.setState,状态发生改变(并不是一次setState会触发一次render,React可能会合并操作,再一次性进行render)
  3. 父组件发生更新(一般就是props发生改变,但是就算props没有改变或者父子组件之间没有数据交换也会触发render)
  4. 调用this.forceUpdate,强制更新

用图来表示这四种方式如下:


更新组件方式.png

四、总结

1. 组件生命周期总体流程图

组件的生命周期.png

2. 生命周期的回调函数总结

|生命周期 |调用次数 |能否使用 setSate() |
|: ------:|:------:|:------:|
|defaultProps / getDefaultProps| 1(全局调用一次) | 否
|constructor / getInitialState |1 |否
|componentWillMount |1 |
|render |>=1 |否
|componentDidMount |1 |
|componentWillReceiveProps |>=0 |
|shouldComponentUpdate |>=0 |否
|componentWillUpdate |>=0 |否
|componentDidUpdate |>=0 |否
|componentWillUnmount |1 |否


这篇文章参考了网上很多文章写出来的,特别是结合ES6的不同点一起写的。感觉还是挺有意义的,有不对的地方欢迎指出哈,欢迎大家提出建议。
其实如果是iOS开发人员,我觉得将组件的生命周期里的调用函数比较iOS的VC中的viewWillAppear等方法,还是挺容易理解的。(安卓应该也会有对应的概念才对)
如果感觉看了还是不太熟悉,建议自己写个demo,把所有方法都实现一次,控制台打出对应log,就一定可以更深刻的理解的!

正在写React Native的学习教程ing,是一边研究一边编写的,已有的成果如下(不断更新哈,望鼓励):
1) React Native 简介与入门
2) React Native 环境搭建和创建项目(Mac)
3) React Native 开发之IDE

4) React Native 入门项目与解析
5) React Native 相关JS和React基础
6) React Native 组件生命周期(ES6)
7) React Native 集成到原生项目(iOS)
8) React Native 与原生之间的通信(iOS)
9) React Native 封装原生UI组件(iOS)

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

推荐阅读更多精彩内容