React Native Android - 基本结构

21 Dec 2015

接上集~

初始化项目后进入目录,里面的结构大概是这样的(忽略隐藏文件):

  • android(directory)
  • ios(directory)
  • index.android.js
  • index.ios.js
  • package.json

Android和ios俩文件夹分别是对应系统的配置文件,深入的分析将在下次分享(其实是还没有研究);android.index.js和index.ios.js分别是android和ios应用的入口;package.json是项目的依赖配置文件,新的add-ons都需要写进这个文件中。

整个项目的js代码是采用reactjs的架构,不熟悉的童鞋可以先go through一下reactjs的官网。 简单地说就是把app与数据分离并且组件化,使组件可复用。我用RN写了一个android的demo,github地址是:这里,下面我就以demo为例分析一下其基本结构。

index.android.js

'use strict';

var React = require('react-native');
var AppMain = require('./components/nav');
var {
  AppRegistry,
  ListView,
  StyleSheet,
  Text,
  View,
} = React;

var App = React.createClass({
    
  render: function() {
    return (            
      <View style=>
        <AppMain />
      </View>
    );
  }
});

AppRegistry.registerComponent('ReactTODO', () => App);

RN中,module的引用用的是require,这里第1,2个var分别引入了React和我在components下面自建的module。第三个var里面是这个文件用到的原生APIs和components,所有的原生组件可以去RN的官网查询,例如Image。需要注意的是目前RN for Android还处于完善阶段,原生的components比IOS的要少很多(带IOS的原生组件只support IOS),还有不少坑要填。AppRegistry是js entry;最后一行AppRegistry.registerComponent就是用来用来注册和运行app的;ListView,Text和View分别是列表,文字和视图的组件;StyleSheet是样式的组件。

React.createClass这个方法创建了叫App的组件,最后被AppRegistry调用运行。下面看另外一个自定义的component:

detail.js

'use strict';

var React = require('react-native');
var apiList = require('../api');
var CommentList = require('./commentList');
var CommentInput = require('./commentInput');

var {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  ListView,
} = React;

var DetailPage = React.createClass({
    getInitialState: function() {
        return {
            dataSource: new ListView.DataSource({
                rowHasChanged: (row1, row2) => row1 !== row2,
            }),
        };
    },

    componentDidMount: function() {
        this.fetchData();
    },

    fetchData: function() {
        fetch(apiList.apiWrapper('comments', String(this.props.todo.id)))
            .then((response) => response.json())
            .then((responseData) => {
                this.setState({
                    dataSource: this.state.dataSource.cloneWithRows(responseData),
                    loaded: true,
                });
            })
            .done();
    },

    updateComment: function() {
        this.fetchData()
    },

    render: function() {
        return (
            <View style={styles.container}>
            <View style={styles.tag}>
            <Text style={styles.tagName}>{this.props.todo.tag}</Text>
            </View>
            <View style={styles.task}>
            <Text>{this.props.todo.task}</Text>
            </View>
            <View style={styles.cell}>
            <View style={styles.name}>
            <Text>{this.props.todo.name}</Text>
            </View>
            <View style={styles.time}>
            <Text>{this.props.todo.time}</Text>
            </View>
            </View>
            <View style={styles.header}>
            <Text style={styles.headerText}>Comments</Text>
            </View>
            <CommentList 
            dataSource={this.state.dataSource} />
            <CommentInput
            todo={this.props.todo} 
            updateComment={this.updateComment} />
            </View>
            );
    },
});

var styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: 'white',
  },
  cell: {
    flexDirection: 'row',
    borderBottomWidth: 0.5,
    borderBottomColor: '#f5f5f5',
    paddingTop: 15,
    paddingBottom: 15,
    paddingLeft: 20,
  },
  task: {
    paddingTop: 15,
    paddingBottom: 15,
    borderBottomWidth: 0.5,
    borderBottomColor: '#f5f5f5',
    paddingLeft: 20,
    paddingRight: 20,
  },
  tag: {
    paddingTop: 15,
    paddingBottom: 15,
    borderBottomWidth: 0.5,
    borderBottomColor: '#f5f5f5',
    paddingLeft: 20,
  },
  tagName: {
    color: '#C74433',
  },
  time: {
    paddingRight: 20,
  },
  name: {
    flex: 1,
  },
  header: {
    paddingTop: 10,
    paddingBottom: 10,
    paddingLeft: 20,
    backgroundColor: '#f5f5f5',
  },
  headerText: {
    fontSize: 11,
  },
});

module.exports = DetailPage;

这个DetailPage是一个比较完整的组件,最后module.exports使得其他模块可以调用DetailPage。在createClass中,有几个方法需要注意:

  • getInitialState:初始化函数,返回需要初始化的变量。
  • componentDidMount:Component load好以后执行的函数。这里定义了一个fetchData()用来拿RESTFul API的数据。有关fetch可以参阅官方文档
  • componentWillMount:这个没有定义但也很重要,看名字就知道是component load之前执行的函数。
  • render:返回组件结构的函数。

StyleSheet.create创建了样式供render的组件调用。

最后关于数据传输,沿用的是react中props和statee,有关于两者的区别和用法可以参阅官方的BLOG:Props and state

comments powered by Disqus