React Native官方入門教程
官方教程主要是一步一步的介紹如何用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方法來渲染我們上面提到的(模擬)資料。
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',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
我們使用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, {
Component,
} from 'react';
import {
AppRegistry,
Image,
ListView,
StyleSheet,
Text,
View,
} 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方法,相應地更新state值。
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,
backgroundColor: '#F5FCFF',
},
最終的效果:
最終的程式碼:
/**
* Sample React Native App
* https://github.com/facebook/react-native
*/
import React, {
Component,
} from 'react';
import {
AppRegistry,
Image,
ListView,
StyleSheet,
Text,
View,
} 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),
loaded: true,
});
})
.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,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
rightContainer: {
flex: 1,
},
title: {
fontSize: 20,
marginBottom: 8,
textAlign: 'center',
},
year: {
textAlign: 'center',
},
thumbnail: {
width: 53,
height: 81,
},
listView: {
paddingTop: 20,
backgroundColor: '#F5FCFF',
},
});
AppRegistry.registerComponent('AwesomeProject', () => AwesomeProject);