此文是我的出版書籍《React Native 精解與實戰》連載分享,此書由機械工業出版社出版,書中詳解了 React Native 架構底層原理、React Native 組件布局、組件與 API 的介紹與代碼實戰,以及 React Native 與 iOS、Android 平台的混合開發底層原理講解與代碼實戰示範,精選了大量執行個體代碼,方便讀者快速學習。
書籍還配套了視頻教程「80 節實戰課精通 React Native 開發」,此視頻課程建議配合書籍學習,書籍中原理性的東西講解的比較清晰,而視頻教程對於組件、API 等部分的代碼實戰開發講解比較直觀。
書籍相關所有資料請訪問:http://rn.parryqiu.com
8.4 React Native 網路請求與列表綁定方案
下面我們就通過結合 Fetch API 以及 React Native 架構中的列表組件,通過代碼實戰的形式進行這兩個重要知識點的學習。
首先我們資料擷取使用豆瓣的公開 API,擷取目前正在上映的 20 部電影的資訊,豆瓣 API 地址為:api.douban.com/v2/movie/in_theaters?count=20,API 介面返回的 JSON 資料 8-6 所示。
圖 8-6 豆瓣 API 返回的 JSON 資料
在此範例程式碼中,將採用組件開發的思想,首頁載入 4 個 Tab,每一個 Tab 載入對應的頁面組件。這裡的列表載入在第一個 Tab 中,組件定義為 Home,在項目中建立的檔案名稱為 home.js。
完整代碼在本書配套源碼的 08-03 檔案夾。
1. /** 2. * 章節: 08-03 3. * App.js 定義了項目的大結構,使用 4 個 Tab 進行布局。 4. * FilePath: /08-03/ListDemo/App.js 5. * @Parry 6. */ 7. 8. import React, {Component} from 'react'; 9. import {Platform, StyleSheet, Text, View, Image} from 'react-native'; 10. import TabNavigator from 'react-native-tab-navigator'; 11. import HomePage from './home'; 12. 13. export default class App extends Component < {} > { 14. 15. state = { 16. selectedTab: 'home' 17. }; 18. 19. _renderContent = (color : string, index : string) => { 20. switch (index) { 21. case "home": 22. return (<HomePage/>); 23. } 24. }; 25. 26. render() { 27. return ( 28. <TabNavigator> 29. <TabNavigator.Item 30. selected={this.state.selectedTab === 'home'} 31. title="首頁" 32. renderIcon={() => <Image 33. style= { {34. width: 25, 35. height: 25 36. } } />37. source={require('./flux.png')}/>} 38. renderSelectedIcon={() => <Image 39. style= { { 40. width: 25, 41. height: 25 42. } } />43. source={require('./relay.png')}/>} 44. onPress={() => this.setState({selectedTab: 'home'})}> 45. {this._renderContent('#FFFFFF', 'home')} 46. </TabNavigator.Item> 47. 48. ...... //此處省略了其他三個 Tab 的定義 49. //完整代碼在書籍的配套源碼中 50. 51. </TabNavigator> 52. ); 53. } 54. }
上面這段代碼為 App.js 的部分主要邏輯,注意在代碼的第 11 行匯入外部 Home 組件的方法,以及針對之前 Tab 組件章節的邏輯修改了載入對應組件的方法,主要為代碼第 20 行的部分。
1. /** 2. * 章節: 08-03 3. * home.js 定義了第一個 Tab 載入的頁面組件,用於載入豆瓣電影列表 4. * 同時示範了 ListView Binder 方法 5. * FilePath: /08-03/ListDemo/home.js 6. * @Parry 7. */ 8. 9. import React, {Component} from 'react'; 10. import { 11. Platform, 12. StyleSheet, 13. Text, 14. View, 15. Image, 16. ListView, 17. SafeAreaView 18. } from 'react-native'; 19. 20. export default class HomePage extends Component < {} > { 21. 22. constructor(props) { 23. super(props); 24. this.state = { 25. dataSource: new ListView.DataSource({ //定義資料來源 26. rowHasChanged: (row1, row2) => row1 !== row2 27. }), 28. loaded: false 29. }; 30. } 31. 32. componentDidMount() { 33. this.fetchData(); //開始請求資料 34. }; 35. 36. fetchData() { 37. fetch("api.douban.com/v2/movie/in_theaters").then((response) => response.json()).then((responseData) => { 38. this.setState({ 39. dataSource: this 40. .state 41. .dataSource 42. .cloneWithRows(responseData.subjects), //讀取返回的所有電影資料 43. loaded: true 44. }); 45. }).done(); 46. }; 47. 48. render() { 49. return ( 50. <View style={styles.container}> 51. <ListView automaticallyAdjustContentInsets={false} //此選項可以修複掉會自動多出來的大約 10px 的空行 52. dataSource={this.state.dataSource} renderRow={this._renderRow}/> 53. </View> 54. ); 55. }; 56. 57. _renderRow(rowData, sectionID, rowID) { 58. return ( 59. <SafeAreaView> 60. <View style={styles.row}> 61. <Image 62. style={styles.thumb} 63. source= { { 64. uri: rowData.images.large 65. } } /> 66. <View style={styles.texts}> 67. <Text style={styles.textTitle}> 68. {rowData.title} 69. </Text> 70. <Text style={styles.textTitle}> 71. 年份: {rowData.year} 72. </Text> 73. <Text style={styles.textTitle}> 74. 豆瓣評分: {rowData.rating.average} 75. </Text> 76. </View> 77. </View> 78. <View style={styles.separator}/> 79. </SafeAreaView> 80. ); 81. }; 82. } 83. 84. var styles = StyleSheet.create({ 85. container: { 86. flex: 1 87. }, 88. row: { 89. flexDirection: 'row', 90. padding: 10 91. }, 92. separator: { 93. height: 1, 94. backgroundColor: '#EEEEEE' 95. }, 96. thumb: { 97. width: 60, 98. height: 80, 99. borderRadius: 2 100. }, 101. textTitle: { 102. flex: 1, 103. textAlign: "left", 104. paddingLeft: 10, 105. fontWeight: "bold", 106. flexDirection: 'row', 107. color: "#666666" 108. }, 109. texts:{ 110. flexDirection: 'column', 111. paddingTop: 5 112. } 113. });
上面代碼為 Home 組件的實現方法,下面主要對代碼中的一些重要邏輯作一些說明:
代碼在 17 行匯入了一個新的 View 組件,SafeAreaView 用於在 iPhone X 下布局 View 而控制整個 View 安全布局於手機的可視地區中;
代碼的第 25 - 27 行,定義了 ListView 的資料來源,同時定義了 rowHasChanged 的邏輯;
代碼第 32 行在生命週期 componentDidMount 中定義了從 API 中載入資料的方法;
代碼第 36 - 46 行定義了從豆瓣 API 使用 Fetch API 請求資料的方法,注意對 Fetch API 返回的 Promise 對象的處理方法;
代碼第 51 行定義了 ListView 綁定的方法,行渲染的方法為代碼中第 57 行定義的方法 _renderRow;
代碼第 57 - 81 行定義了列表渲染的方法,使用 View 與 Text 組件進行了列表的展示布局;
後續的樣式定義如之前學習的樣式定義一樣,進行精細布局控制即可。
項目運行在 iOS 平台的效果 8-7 所示,Android 平台大家也可以直接下載本書配套源碼在本地學習、測試與運行。
圖 8-7 iOS 下的 ListView 運行效果
8.5 本章小結
列表綁定是 App 開發最常用的一個開發功能,你可以隨手開啟自己手機上的 App 就會發現許多 App 的首頁都是進行了資料請求、列表綁定或列表資料重新整理等動作,這也真是移動互連網的魅力所在,使用者可以隨時擷取到最新的資訊資訊。所以此章節是一個重要的章節,並從底層知識點到實戰代碼都進行了詳細地講解與示範,希望能協助你開發出你的 App 的首頁列表組件。