主题:组件的生命周期
RN组件的props和state
1.属性(props)
它是组件的不可变属性(组件自己不可以自己修改props)。
组件自身定义了一组props作为对外提供的接口,展示一个组件时只需要指定props作为节点的属性。
一般组件很少需要对外公开方法(例外:工具类的静态方法等),唯一的交互途径就是props。所以说它也是父组件与子组件通信的桥梁。
组件自己不可以自己修改props(即:props可认为是只读的),只可由其他组件调用它时在外部修改。
2.状态(state)
它是组件的内部状态属性,主要用来存储组件自身需要的数据。
除了初始化时可能由props来决定,之后就完全由组件自身去维护。
组件中由系统定义了setState方法,每次调用setState时都会更新组件的状态,触发render方法重新渲染界面。
需要注意的是render方法是被异步调用的,这可以保证同步的多个setState方法只会触发一次render,这样做是有利于提高性能的。
RN生命周期
可以把组件生命周期大致分为四个阶段:
- 第一阶段:创建初始化组件的属性类型和默认属性;
- 第二阶段:组件类被调用(实例化)
- 第三阶段:是组件在运行和交互阶段,如图中左下角虚线框,这个阶段组件可以处理用户交互,或者接收事件更新界面;
- 第四阶段:是组件卸载消亡的阶段,如图中右下角的虚线框中,这里做一些组件的清理工作
1.创建阶段
该阶段主要发生在创建组件类的时候,主要作用是创建初始化组件的属性类型和默认属性。
defaultProps / getDefaultProps()
这里用来初始化默认属性,一个控件通过this.props获取需要的属性。因为组件初始化后不会再次调用本方法,所以组件自己不能更改自身的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,
}; // 注意这里有分号
替换方法
npm install --save prop-types
import PropTypes from 'prop-types';
属性: PropTypes.array,
属性: PropTypes.bool,
属性: PropTypes.func,
属性: PropTypes.number,
属性: PropTypes.object,
属性: PropTypes.string,
2.实例化阶段
该阶段主要发生在组件类被调用(实例化)的时候。
1) constructor(props) / getInitialState()
这里是对控件的一些状态进行初始化,由于该函数不同于getDefaultProps,在以后的过程中,会再次调用,所以可以将控制控件的状态的一些变量放在这里初始化,如控件上显示的文字,可以通过this.state来获取值,通过this.setState来修改state值。
在ES5里,通过getInitialState对状态进行初始化
//ES5
getInitialState: function() {
return {
loopsRemaining: this.props.maxLoops,
};
},
在ES6里,通过constructor(构造器)对状态进行初始化
//ES6
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() 来更新界面了。
==绝对不要在componentWillUpdate和componentDidUpdate中调用this.setState方法,否则将导致无限循环调用。!!!==
4) render()
再确定需要更新组件时,调用render,根据diff算法,渲染界面,生成需要更新的虚拟DOM数据。
5) componentDidUpdate()
虚拟DOM同步到DOM中后,执行该方法,可以在这个方法中做DOM操作。
除了首次render之后调用componentDidMount,其它render结束之后都是调用componentDidUpdate。
==绝对不要在componentWillUpdate和componentDidUpdate中调用this.setState方法,否则将导致无限循环调用。!!!==
4. 销毁阶段
该阶段主要发生组件销亡的时候,触发componentWillUnmount。当组件需要从DOM中移除的时候,通常需要做一些取消事件绑定,移除虚拟DOM中对应的组件数据结构,销毁一些无效的定时器等工作,都可以在这个方法中处理。
componentWillUnmount()
当组件要被从界面上移除的时候,就会调用 componentWillUnmount。
在这个函数中,可以做一些组件相关的清理工作,例如取消计时器、网络请求等。
组件更新时机
更新组件(重新渲染界面)的方式有以下四种:
- 首次渲染Initial Render,即首次加载组件
- 调用this.setState,状态发生改变(并不是一次setState会触发一次render,React可能会合并操作,再一次性进行render)
- 父组件发生更新(一般就是props发生改变,但是就算props没有改变或者父子组件之间没有数据交换也会触发render)
- 调用this.forceUpdate,强制更新
生命周期 | 调用次数 | 能否使用 setSate() |
---|---|---|
defaultProps / getDefaultProps | 1(全局调用一次) | 否 |
constructor / getInitialState | 1 | 否 |
componentWillMount | 1 | 是 |
render | >=1 | 否 |
componentDidMount | 1 | 是 |
componentWillReceiveProps | >=0 | 是 |
shouldComponentUpdate | >=0 | 否 |
componentWillUpdate | >=0 | 否 |
componentDidUpdate | >=0 | 否 |
componentWillUnmount | 1 | 否 |
tips:webStorm添加RN提示插件
进入你想存储这个文件的目录,按住shift+鼠标右键,选择“在此处打开命令窗口”
在命令窗口中输入 git clone https://github.com/virtoolswebplayer/ReactNative-LiveTemplate
打开webstorm, 选择file-->Import Settings...-->在刚刚下载的文件夹里找到ReactNative.jar选择它-->OK
提示重启webstrom,重启,搞定