官方教程主要是一步一步的介绍如何用ListView来显示一条Json数据。
模拟数据
在编写关于获取远程数据的代码之前,先让我们用一些模拟数据,以便上手React Native。在Facebook(公司),我们的做法是一般在JS文件的顶部声明常量,下面接着就是一些import。当然你可以在index.ios.js或index..android.js(这里考虑android)的任意位置增加下面的常量:
var MOCKED_MOVIES_DATA = [ {title: 'Title',year: '2015',posters: {thumbnail: 'http://i.imgur.com/UePbdph.jpg'}},];
渲染一个movie
我们现在为这个movie渲染title,year和thumbnail。由于thumbnail在React Native中是一个Image组件,因此增加Image到下面React imports列表。
import React,{
Component,} from 'react';
import {
AppRegistry,Image,StyleSheet,Text,View,} from 'react-native';
render() { var movie = MOCKED_MOVIES_DATA[0]; return ( <View style={styles.container}> <Text>{movie.title}</Text> <Text>{movie.year}</Text> <Image source={{uri: movie.posters.thumbnail}} /> </View> ); }
手机上打开开发者菜单Reload JS后你应该可以看到“2015”和下面的“Title”。注意,Image没有被渲染上。这是因为我们没有为其制定宽和高。这需要通过styles来实现。让我们清除不在使用的styles再修改styles。
var styles = StyleSheet.create({ container: { flex: 1,justifyContent: 'center',alignItems: 'center',backgroundColor: '#F5FCFF',},thumbnail: { width: 53,height: 81,});
同时,我们需要将style应用到Image组件上。
<Image source={{uri: movie.posters.thumbnail}} style={styles.thumbnail} />
重新Reload JS,我们会看到下面的效果:
增加一些样式
上面渲染了我们的数据,现在让我们使他看起来更好,我打算在图片的右边放一些文字,让title变大并且居中。
我们需要增加一个容器以便我们在水平方向上布局我们的组件。
return ( <View style={styles.container}> <Image source={{uri: movie.posters.thumbnail}} style={styles.thumbnail} /> <View style={styles.rightContainer}> <Text style={styles.title}>{movie.title}</Text> <Text style={styles.year}>{movie.year}</Text> </View> </View> );
没有太多的改变,我们增加了一个容器包含所有的Text并把这些Text移动Image的下面(因为他们是在Image的右边显示)。然后我们看看style做的修改:
container: { flex: 1,flexDirection: 'row',},
我们使用FlexBox(弹性盒模型)来布局-关于FlexBox去this great guide了解更多。
在上面的代码片段中,我们增加了flexDirection:’row’,这使得容器里的子view在水平方向上布局而不是在垂直方向上。
现在在Js style object中增加另一个style:
rightContainer: { flex: 1,
这意味着rightContainer占据父容器中Image以外的剩余的空间,如果你感觉效果不明显,可以给rightContainer增加一个backgroundColor并移除flex:1。你将会看到容器尺寸变成了能容纳子view的最小尺寸。
简单的文字样式:
title: { fontSize: 20,marginBottom: 8,textAlign: 'center',year: { textAlign: 'center',
重新Reload JS,你将看到新的视图
取得真正的数据
从Rotten Tomatoes’s API是取得数据是真正的开始学习React Native,因此我们在这块尽情畅游。
在JS文件顶部增加下面的常量,在请求数据的时候需要使用。
/** * For quota reasons we replaced the Rotten Tomatoes' API with a sample data of * their very own API that lives in React Native's Github repo. */
var REQUEST_URL = 'https://raw.githubusercontent.com/facebook/react-native/master/docs/MoviesExample.json';
为我们的应用增加一些初始的状态,我们可以判断this.state.movies == null ,根据movies是否为null来决定movies data是否已经加载完成。当请求响应回来时,使用this.setState(movies:moviesData)来设置数据。在React类里render方法上增加这些代码:
constructor(props) { super(props); this.state = { movies: null,};
}
我们要在组件加载完成后发送一个请求。componentDimMount是React components方法,React仅仅在组件加载完成后调用一次该方法。
componentDidMount() { this.fetchData(); }
现在再增加fetchData方法。这个方法用于获取数据。你需要做的只是在解析了协议之后调用this.setState({movies:data}),因为React工作的方式是:setState方法会触发render方法重新渲染,这时render方法里this.setstate.movies将不再是null。注意,我们在方法链最好调用了done()方法,一般总是需要调用done()方,否则任何抛出的异常你将都看不到。
fetchData() {
fetch(REQUEST_URL)
.then((response) => response.json()) .then((responseData) => { this.setState({ movies: responseData.movies,}); }) .done(); }
现在修改render()方法,如果没有movies data就渲染loading view否则渲染第一个movies(获取到的json数据包含多个movies)。
render() { if (!this.state.movies) { return this.renderLoadingView(); } var movie = this.state.movies[0]; return this.renderMovie(movie); } renderLoadingView() { return ( <View style={styles.container}> <Text> Loading movies... </Text> </View> ); } renderMovie(movie) { return ( <View style={styles.container}> <Image source={{uri: movie.posters.thumbnail}} style={styles.thumbnail} /> <View style={styles.rightContainer}> <Text style={styles.title}>{movie.title}</Text> <Text style={styles.year}>{movie.year}</Text> </View> </View> ); }
重新reload JS,你将看到“Loading movies…”直到响应返回,才会渲染从Rotten Tomatoes取得的第一条movie。
ListView
现在修改应用程序,我们使用ListView组件来渲染所有的数据,而不仅仅显示出第一条movie。
为什么使用ListView更好,而不是仅仅渲染所有这些数据或者使用ScrollView?尽管React是快速的,但是渲染一个可能无限的元素列表是慢的。使用ListView渲染views,你仅仅展现一整个屏幕的元素,但是屏幕以外(看不到的)的views会从native view的层次结构上删除。
首先:添加ListView到import中。
import React,ListView,} from 'react-native';
接着修改render方法,一旦获取数据就使用ListView渲染所有movie,而不是仅仅一条movie。
render() {
if (!this.state.loaded) {
return this.renderLoadingView();
}
return (
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderMovie}
style={styles.listView}
/>
);
}
dataSource是一个接口,ListView用以决定哪一行在更新过程中改变过。
注意我们使用this.state的dataSource。接下来增加一个空的dataSource到constructor里面。现在我们正在给dataSource里的数据排序,我们不应该再使用this.state.movies已避免数据排序两次。我们使用boolean类型的属性(this.state.loaded)来判断数据是否加载完成。
constructor(props) { super(props); this.state = { dataSource: new ListView.DataSource({ rowHasChanged: (row1,row2) => row1 !== row2,}),loaded: false,};
}
fetchData() {
fetch(REQUEST_URL)
.then((response) => response.json()) .then((responseData) => { this.setState({ dataSource: this.state.dataSource.cloneWithRows(responseData.movies),loaded: true,}); }) .done(); }
最后我们为ListView增加样式。
listView: { paddingTop: 20,
/**
* Sample React Native App
* https://github.com/facebook/react-native
*/
import React,} from 'react';
import {
AppRegistry,} from 'react-native';
var REQUEST_URL = 'https://raw.githubusercontent.com/facebook/react-native/master/docs/MoviesExample.json';
class AwesomeProject extends Component {
constructor(props) {
super(props);
this.state = {
dataSource: new ListView.DataSource({
rowHasChanged: (row1,row2) => row1 !== row2,}),loaded: false,};
}
componentDidMount() {
this.fetchData();
}
fetchData() {
fetch(REQUEST_URL)
.then((response) => response.json()) .then((responseData) => { this.setState({ dataSource: this.state.dataSource.cloneWithRows(responseData.movies),}); }) .done(); } render() { if (!this.state.loaded) { return this.renderLoadingView(); } return ( <ListView dataSource={this.state.dataSource} renderRow={this.renderMovie} style={styles.listView} /> ); } renderLoadingView() { return ( <View style={styles.container}> <Text> Loading movies... </Text> </View> ); } renderMovie(movie) { return ( <View style={styles.container}> <Image source={{uri: movie.posters.thumbnail}} style={styles.thumbnail} /> <View style={styles.rightContainer}> <Text style={styles.title}>{movie.title}</Text> <Text style={styles.year}>{movie.year}</Text> </View> </View> ); } } var styles = StyleSheet.create({ container: { flex: 1,rightContainer: { flex: 1,title: { fontSize: 20,year: { textAlign: 'center',listView: { paddingTop: 20,}); AppRegistry.registerComponent('AwesomeProject',() => AwesomeProject);
本文译自:https://facebook.github.io/react-native/docs/tutorial.html#content