React Native语法指南

React Native真的是越来越流行,没使用React Native开发项目都不好意思说自己是搞客户端开发的。对于纯Native开发者来说,刚上手React Native有一定的适应期,如果JavaScript也不熟练的话那就更悲催了。React Native涉及ES6,React语法,JSX,前端调试,Native客户端等知识,本文简单总结了React Native开发中一些知识点。算是在学习中的积累。

Component

Component:组件,使用React.createClass或者extends React.Component创建的类为组件。
Element:元素或者可以说是组件的实例,使用<Label />或者let label = new Label()创建的为实例。

对于定义组件,React以前版本的写法(ES5):

1
2
3
4
5
6
var Lable  = React.createClass({

render(){

}
});

React最新的写法(ES6):

1
2
3
4
class Label extends React.Component{
render(){
}
}

props与state

props属性:组件可以定义初始值,自己不可更改props属性值,只允许从父组件中传递过来:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 父组件
class MainComponent extends React.Component{
render(){
return(<Label name="标题栏">);
}
}

// 子组件
class Label extends React.Component{
render(){
return(<Text>{this.props.name}</Text>);
}
}

父组件向Label传递name=”标题栏”的props属性,在Label中使用this.props.name引用此属性。

state属性:组件用来改变自己状态的属性,通常使用setState({key:value})来改变属性值,不能使用this.state.xxx来直接改变,setState({key:value})方法会触发界面刷新。

对于经常改变的数据且需要刷新界面显示,可以使用state。对于不需要改变的属性值可以使用props。React Native建议由顶层的父组件定义state值,并将state值作为子组件的props属性值传递给子组件,这样可以保持单一的数据传递。

在以前版本的React中定义state,props可以使用生命周期方法 getInitialState()getInitialState():

1
2
3
4
5
6
7
8
9
10
11
12
13
var Label = React.createClass({
getInitialState(){
key:value,
...
},
getInitialProps(){
key:value,
...
},// 这种写法需要有,不要使用;
render:funation(){

}
});

在最新版本的React可以使用构造函数替代getInitialState(),getInitialState()方法定义初始值:

1
2
3
4
5
6
7
8
9
10
11
12
class Label extends React.Component{
constructor(props) {
super(props);
this.state = {
time: '2016',
city: '上海',
};
this.props = {
name:'标题',
};
}
}

默认props与props校验

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
class Label extends React.Component{
constructor(props) {
super(props);
}

// 默认props
static defaultProps = {
city: '南京',
index: 12,
}

// propTypes用于验证转入的props,当向 props 传入无效数据时,JavaScript 控制台会抛出警告
static propTypes = {
city: React.PropTypes.string.isRequired,
index: React.PropTypes.number.isRequired,
}

state = {
city: this.props.city,
index:this.props.index,
}
}

// or

class Label extends React.Component{
constructor(props) {
super(props);
}
}

// 默认props
Label.defaultProps = {
city: '南京',
index: 12,
}

// propTypes用于验证转入的props,当向 props 传入无效数据时,JavaScript 控制台会抛出警告
Label.propTypes = {
city: React.PropTypes.string.isRequired,
index: React.PropTypes.number.isRequired,
}

生命周期

我们把组件从装载,到渲染,再到卸载当做一次生命周期,也就是组件的生存状态从装载开始到卸载为止,期间可以根据属性的变化进行多次渲染。

生命周期的三种状态:

  • Mounting:装载,
  • Updating:渲染
  • Unmounting:卸载
1
2
3
4
5
componentWillMount(),组件开始装载之前调用,在一次生命周期中只会执行一次。
componentDidMount(),组件完成装载之后调用,在一次生命周期中只会执行一次,从这里开始就可以对组件进行各种操作了,比如在组件装载完成后要显示的时候执行动画。
componentWillUpdate(object nextProps, object nextState),组件属性更新之前调用,每一次属性更新都会调用
componentDidUpdate(object prevProps, object prevState),组件属性更新之后调用,每次属性更新都会调用
componentWillUnmount(),组件卸载之前调用

组件属性更改时会调用以下方法,在一次生命周期中可以执行多次:

1
2
componentWillReceiveProps(object nextProps),已加载组件收到新的参数时调用
shouldComponentUpdate(object nextProps, object nextState),组件判断是否重新渲染时调用

页面跳转

初始化第一个页面:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import SeatPageComponent from './SeatPageComponent';
import MainPageComponent from './MainPageComponent';
import TrainListComponent from './TrainListComponent';

class MainPage extends React.Component {
render() {
let defaultName = 'MainPageComponent';
let defaultComponent = MainPageComponent;
return (
<Navigator
// 指定默认页面
initialRoute={{ name: defaultName, component: defaultComponent }}
// 配置页面间跳转动画
configureScene={(route) => {
return Navigator.SceneConfigs.VerticalDownSwipeJump;
}}
// 初始化默认页面
renderScene={(route, navigator) => {
let Component = route.component;
// 将navigator作为props传递到下一个页面
return <Component {...route.params} navigator={navigator} />
}} />
);
}
}

跳转到下一页面:

1
2
3
4
5
6
7
8
9
jumpToNext(){
const { navigator } = this.props;// 由上一个页面传递过来
if(navigator) {
navigator.push({
name: 'SeatPageComponent',
component: SeatPageComponent,// 下一个页面
});
}
}

返回上一个页面:

1
2
3
4
5
6
_back(){
const { navigator } = this.props;
if(navigator) {
navigator.pop();
}
}

页面间通信

例如:从A页面打开B页面
A通过route.params将参数传递给B:

1
2
3
4
5
6
7
8
9
10
11
12
13
jumpToNext(){ 
const { navigator } = this.props;// 由上一个页面传递过来
if(navigator) {
navigator.push({
name: 'SeatPageComponent',
component: SeatPageComponent,// 下一个页面
params: { // 需要传递个下一个页面的参数,第二个页面使用this.props.xxx获取参数
id: 123,
title: this.state.title,
},
});
}
}

A通过route.params传递回调方法或者A的引用来让B将数据传回给A:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

// A页面
jumpToNext(){
const { navigator } = this.props;// 由上一个页面传递过来
if(navigator) {
let that = this;// this作用域,参见下文函数绑定
navigator.push({
name: 'SeatPageComponent',
component: SeatPageComponent,// 下一个页面
params: { // 需要传递个下一个页面的参数,第二个页面使用this.props.xxx获取参数
title: '测试',
getName: function(name) {that.setState({ name: name })}
},
});
}
}

// B页面
_back(){
const { navigator } = this.props;
if(this.props.getName){
this.props.getName('测试');
}
if(navigator) {
navigator.pop();
}
}

组件间通信

父组件–>子组件, 使用props,父组件向子组件传递props

1
2
3
4
5
6
7
8
9
10
11
12
13
// 父组件
class MainComponent extends React.Component{
render(){
return(<Label name="标题栏">);
}
}

// 子组件
class Label extends React.Component{
render(){
return(<Text>{this.props.name}</Text>);
}
}

子组件–>父组件, 父组件在创建子组件时传递回调方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 父组件
class MainComponent extends React.Component{
constructor(props) {
super(props);
this.state = {
name: '测试',
};
}

// 回调方法
getName(str){
this.setState({name:str});
}

render(){
return(<Label name="标题栏" getName={getName}/>);
}
}

// 子组件
class Label extends React.Component{
render(){
return(
<View>
<TouchableOpacity onPress={()=>this._onPress()}>
<Text>点我,{this.props.name}</Text>
</TouchableOpacity>
</View>);

}

_onPress(){
if(this.props.getName){
this.props.getName('测试')
}
}
}

非父子关系的组件,即没有任何嵌套关系的组件, 可以引入订阅源(js-signals, PubSubJS),监听订阅事件。例如,在生命周期方法中addEventListener(),removeEventListener(),在合适时机setState()。

ECMAScript

ES6中函数的写法:

1
2
3
4
5
class Label extends React.Component{
doSomething(){
//...
}// 不要使用逗号或者分号作为结尾
}

key:value形式定义函数的写法:

1
2
3
4
5
6
7
8
var Label = React.createClass({
doSomething:funation(){
//......
},// 需要使用逗号作为结尾,不能使用分号
doSomething2:function(){
//......
},
});

函数绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Label extends React.Component{

// 有函数
sayHello(str){
console.log(str)
}

// 在onPress中使用箭头函数调用
// onPress={() => this.sayHello('Hello')}

// 等同于
//onPress={sayHello('hello').bind(this)}

// 等同于
// onPress={print('hello',this)}

render(){
return (
<View>
<TouchableOpacity onPress={() => this.sayHello('Hello')}>
<Text>点我</Text>
</TouchableOpacity>
</View>
)

}

function print(str,this){
let that = this;// 注意这里this的生命周期
function say(str){
that.sayHello(str)// 此处不能再使用this
}
say(str);
}
}

Tips

require,import:javascript的模块管理工具,管理各个模块之间的引用,解决javascript异步加载的问题,解决js写成多个文件后浏览器加载缓慢的问题。

JavaScript中没有private,public的概念
使用_开头的方法代表private方法,不适用则表示public方法

1
2
3
4
5
6
7
8
9
10
11
class Label extends Component{
// private 函数
_doSomething(){
//......
}

// public 函数
doSomething(){
//......
}
}


参考资料
Reactjs中文教程
极客学院React教程
ECMAScript语法
JavaScript模块系统
require.js
Navigator
结合ES6+开发React
React组件通信