ReactNavtive架構教程(4),
原文:http://www.raywenderlich.com/99473/introducing-react-native-building-apps-javascript
注意:所有圖片放在了百度相簿空間,如果你看不到圖片,請複製圖片URL,然後粘貼到地址欄中進行查看。
開始搜尋
為了實現搜尋概念,我們需要處理Go按鈕點擊事件,建立對應的API請求,顯示網路請求的狀態。
開啟SearchPage.js, 在constructor方法中修改state的初始化代碼:
this.state = { searchString: 'london', isLoading: false }; |
isLoading 屬性用於表示查詢是否進行中。
在render方法最上面增加:
var spinner = this.state.isLoading ? ( <ActivityIndicatorIOS hidden='true' size='large'/> ) : ( <View/>); |
用了一個三目運算,這是一個if語句的簡單形式。如果isLoading為true,顯示一個網路指標,否則顯示一個空的view。
在return語句中,在Image下增加:
在Go按鈕對應的 TouchableHighlight 標籤中增加如下屬性:
onPress={this.onSearchPressed.bind(this)} |
在 SearchPage 類中新增如下方法:
_executeQuery(query) { console.log(query); this.setState({ isLoading: true }); } onSearchPressed() { var query = urlForQueryAndPage('place_name', this.state.searchString, 1); this._executeQuery(query); } |
_executeQuery() 目前僅僅是在控制台中輸出一些資訊,同時設定isLoading屬性為true,剩下的功能我們留到後面完成。
注意: JavaScript類沒有訪問器,因此也就沒有私人的概念。因此我們會在方法名前加一個底線,以表示該方法視同為私人方法。
當Go按鈕被點擊, onSearchPressed() 即被調用。
在 SearchPage 類聲明之前,聲明如下實用函數:
function urlForQueryAndPage(key, value, pageNumber) { var data = { country: 'uk', pretty: '1', encoding: 'json', listing_type: 'buy', action: 'search_listings', page: pageNumber }; data[key] = value; var querystring = Object.keys(data) .map(key => key + '=' + encodeURIComponent(data[key])) .join('&'); return 'http://api.nestoria.co.uk/api?' + querystring; }; |
這個函數不依賴SearchPage類,因此被定義為函數而不是方法。它首先將key\value參數以索引值對形式放到了data集合中,然後將data集合轉換成以&符分隔的“鍵=值”形式。=>文法是箭頭函數的寫法,一種建立匿名函數簡潔寫法,具體請參考
recentaddition to the JavaScript language 。
回到模擬器,按下 Cmd+R 重啟App,然後點擊‘Go’ 按鈕。你將看到網路指標開始轉動。同時控制台將輸出:
網路指標顯示,同時要請求的URL也列印出來了。拷貝並粘貼URL到Safari,查看搜尋結果。你將看到一堆JSON對象。我們將用代碼解析這些JSON對象。
注意: 這個App使用 Nestoria API 來尋找房子。尋找結果以JSON格式返回。官方文檔中列出了所有請求的URL規範及響應格式。
發送請求
開啟 SearchPage.js,在初始化狀態過程中增加一個message屬性:
this.state = { searchString: 'london', isLoading: false, message: '' }; |
在render方法中,在UI元素的最後加入:
<Text style={styles.description}>{this.state.message}</Text> |
這個Text用於向使用者顯示一些文本。
在 SearchPage 類中,在 _executeQuery()方法最後加入:
fetch(query) .then(response => response.json()) .then(json => this._handleResponse(json.response)) .catch(error => this.setState({ isLoading: false, message: 'Something bad happened ' + error })); |
fetch 函數在 Fetch API中定義,這個新的JavaScript規範被Firefox 39(Nightly版)以及Chrome 42(開發版)支援,它在XMLHttpRequest的基礎上進行了極大的改進。結果是非同步返回的,同時使用了 promise規範,如果response中包含有效JSON對象則將JSON對象的response成員(另一個JSON)傳到_handleResponse方法(後面實現)。
然後在 SearchPage類中增加方法:
_handleResponse(response) { this.setState({ isLoading: false , message: '' }); if (response.application_response_code.substr(0, 1) === '1') { console.log('Properties found: ' + response.listings.length); } else { this.setState({ message: 'Location not recognized; please try again.'}); } } |
如果查詢結果成功返回,我們重設 isLoading 屬性為false,然後列印結果集的總數。
注意: Nestoria有 不以1開頭的響應碼, 這些代碼都非常有用。例如202 和 200表示返回一個推薦位置的列表。當完成這個執行個體後,你可以嘗試處理這些返回碼,並將列表提供給使用者選擇。
儲存,返回模擬器,按下Cmd+R ,然後搜尋 ‘london’你將在控制台看到一條訊息,表示搜尋到20條房子資訊。嘗試輸入一個不存在的地名,比如 ‘narnia’ 你將看到如下資訊:
展現搜尋結果
建立一個檔案: SearchResults.js, 編寫如下代碼:
'use strict'; var React = require('react-native'); var { StyleSheet, Image, View, TouchableHighlight, ListView, Text, Component } = React; |
一條requires語句和一個結構賦值。
定義一個Componet子類:
class SearchResults extends Component { constructor(props) { super(props); var dataSource = new ListView.DataSource( {rowHasChanged: (r1, r2) => r1.guid !== r2.guid}); this.state = { dataSource: dataSource.cloneWithRows(this.props.listings) }; } renderRow(rowData, sectionID, rowID) { return ( <TouchableHighlight underlayColor='#dddddd'> <View> <Text>{rowData.title}</Text> </View> </TouchableHighlight> ); } render() { return ( <ListView dataSource={this.state.dataSource} renderRow={this.renderRow.bind(this)}/> ); } } |
上述代碼中使用了一個專門的組件——ListView ——該組件非常像ITableView。通過ListView.DataSource, 我們可以向ListView提供資料。renderRow函數則用於為每個行提供UI。
在構建資料來源的時候,我們使用箭頭函數對不同的行進行識別。這個函數在ListView進行“一致化”的時候被調用,以便判斷列表中的資料是否被改變。在本例中,NestoriaAPI有一個guid屬性,剛好可以用來作為判斷的標準。
最後,加入一條模組輸出語句:
module.exports = SearchResults; |
SearchPage.js 頭部,require 下方加入:
var SearchResults = require('./SearchResults'); |
這樣我們就可以 SearchPage 類中使用SearchResults類了。
在_handleResponse 方法,將console.log 一句替換為:
this.props.navigator.push({ title: 'Results', component: SearchResults, passProps: {listings: response.listings} }); |
上述代碼將導航至SearchResults 頁面,並將請求到的列表資料傳遞給它。Push方法可以將頁面添加到導航控制器的ViewController堆棧中,同時你的導覽列上將出現一個Back按鈕,點擊它可以返回到上一頁面。
回到模擬器, 按下Cmd+R ,進行一個尋找動作。你將看到搜尋結果如下: