1. 程式人生 > >深入淺出 React Native:使用 JavaScript 構建原生應用

深入淺出 React Native:使用 JavaScript 構建原生應用

基於 PhoneGap 使用 JavaScript 和 HTML5 開發 iOS 應用已經有好幾年了,那 React Native 有什麼牛的?

React Native 真的很牛,讓大家興奮異常的主要原因有兩點:

  1. 可以基於 React Native使用 JavaScript 編寫應用邏輯,UI 則可以保持全是原生的。這樣的話就沒有必要就 HTML5 的 UI 做出常見的妥協;

  2. React 引入了一種與眾不同的、略顯激進但具備高可用性的方案來構建使用者介面。長話短說,應用的 UI 簡單通過一個基於應用目前狀態的函式來表達。

React Native 的關鍵就是,以把 React 程式設計模式的能力帶到移動開發來作為主要目標。它的目標不是跨平臺一次編寫到處執行,而是一次學習跨平臺開發。這個是一個非常大的區別。這篇教程只介紹 iOS 平臺,不過你一旦掌握了相關的概念,就可以應用到 Android 平臺,快速構建 Android 應用。

如果之前只用過 Objective-C 或者 Swift 寫應用的話,你很可能不會對使用 JavaScript 來編寫應用的願景感到興奮。儘管如此,作為一個 Swift 開發者來說,上面提到的第二點應該可以激起你的興趣!

你通過 Swift,毫無疑問學習到了新的更多有效的編碼方法和技巧,鼓勵轉換和不變性。然而,構建 UI 的方式還是和使用 Objective-C 的方式一致。仍然以 UIKit 為基礎,獨斷專橫。

通過像 virtual DOM 和 reconciliation 這些有趣的概念,React 將函數語言程式設計直接帶到了 UI 層。

這篇教程將帶著你一路構建一個 UK 房產搜尋應用:

如果你之前一點 JavaScript 都沒寫過,別擔心。這篇教程帶著你進行一步一步進行編碼。React 使用 CSS 屬性來定義樣式,一般比較容易讀也比較容易理解。但是如果你想了解更多的話,可以去看看 Mozilla Developer Network reference,很不錯的。

想要學習更多,繼續往下讀!


準備工作

React Native 框架託管在 GitHub 上。你可以通過兩種方式獲取到它:使用 git 克隆倉庫,或者下載一個 zip 壓縮包檔案。如果你的機器上已經安裝了 React Native,在著手編碼前還有其他幾個因素需要考慮。

  • React Native 藉助 Node.js
    ,即 JavaScript 執行時來建立 JavaScript 程式碼。如果你已經安裝了 Node.js,那就可以上手了。

首先,使用 Homebrew 官網提供的指引安裝 Homebrew,然後在終端執行以下命令:

brew install node

接下來,使用 homebrew 安裝 watchman,一個來自Facebook 的觀察程式:

brew install watchman

通過配置 watchman,React 實現了在程式碼發生變化時,完成相關的重建的功能。就像在使用 Xcode 時,每次儲存檔案都會進行一次建立。

接下來使用 `npm` 安裝 React Native CLI 工具:

npm install -g react-native-cli

這使用 Node 包管理器抓取 CLI 工具,並且全域性安裝;`npm` 在功能上與 CocoaPods 或者 Carthage 類似。

瀏覽到你想要建立 React Native 應用的資料夾下,使用 CLI 工具構建專案:

react-native init PropertyFinder

現在,已經建立了一個初始專案,包含了建立和執行一個 React Native 應用所需的一切。

如果仔細觀察了建立的資料夾和檔案,你會發現一個 node_modules 資料夾,包含了 React Native 框架。你也會發現一個 index.ios.js 檔案,這是 CLI 工具建立的一個空殼應用。最後,會出現一個 Xcode 專案檔案和一個 iOS 資料夾,包含了少量的程式碼用來引入 bootstrap 到你的專案中。

開啟 Xcode 專案檔案,然後建立並執行。模擬器將會啟動並且展示下面的問候語:

你可以能發現,有一個終端視窗彈出,輸出了下面的資訊:

$ npm start

> react-native@0.1.0 start /Users/colineberhardt/Projects/react-native
> ./packager/packager.sh

 ===============================================================
 |  Running packager on port 8081.       
 |  Keep this packager running while developing on any JS         
 |  projects. Feel free to close this tab and run your own      
 |  packager instance if you prefer.                              
 |                                                              
 |     https://github.com/facebook/react-native                 
 |                                                              
 ===============================================================


    React packager ready.

這就是 React Native 包,在 node 下執行。一會兒你就會知道它是用來幹什麼的。

不要關閉終端視窗;就然它在後臺執行。如果你不小心關了,只需要停下來使用 Xcode 重新執行專案。

注意:在進入編碼工作之前,還有最後一件事 —— 在這個教程中,你需要編寫大量的 JavaScript 程式碼,Xcode 並非是最好的工具!我使用 Sublime Text,一個價格合理且應用廣泛的編輯器。不過,atombrackets 或者其他輕量的編輯器都能勝任這份工作。

React Native 你好

在開始“搜房App”之前,先來個簡單的 Hello World App 熱熱身。在這一節裡,你將會使用到一些元件。

在你鍾愛的編輯其中開啟 index.ios.js,刪除當前的內容,因為你要從頭構建你自己的應用。然後在檔案頂部增加下面這樣一行::

'use strict';

這行程式碼是用於開啟 Strict Mode,Strict mode的錯誤處理可以有所提高,JavaScript的一些語言缺陷也可以避免。簡而言之就是,JavaScript在這種模式下工作地更好!

然後,加上這一行:

var React = require('react-native');

這句程式碼是將 react-native 模組載入進來,並將它賦值給變數 React 的。React Native 使用同 Node.js 相同的模組載入方式:require,這個概念可以等同於 Swift 中的“連結庫”或者“匯入庫”。

注意:想要了解更多關於 JavaScript 模組的知識,我推薦閱讀 Addy Osmani 寫的這篇文章

在 require 語句的下面,加上這一段:

var styles = React.StyleSheet.create({
  text: {
    color: 'black',
    backgroundColor: 'white',
    fontSize: 30,
    margin: 80
  }
});

以上程式碼定義了一段應用在 “Hello World” 文字上的樣式。如果你曾接觸過Web開發,那你很可能已經發現了:React Native 使用的是 CSS 來定義應用介面的樣式。

現在我們來關注應用本身吧!依然是在相同的檔案下,將以下程式碼新增到樣式程式碼的下面:

class PropertyFinderApp extends React.Component {
  render() {
    return React.createElement(React.Text, {style: styles.text}, "Hello World!");
  }
}

是的,奏是 JavaScript class!

類 (class) 是在ES6中被引入的,縱然JavaScript一直在進步,但Web開發者受困於相容瀏覽器的狀況中,不能怎麼使用JS的新特性。React Native執行在JavaScriptCore中是,也就是說,你可以使用JS的新特性啦,完全不用擔心相容什麼的呢。

注意:如果你是一名 Web 開發者,我百分百鼓勵你要使用現代的JavaScript,然後使用像 Babel 這樣的工具生成相容性的 JavaScript,用於支援相容性不好的老瀏覽器。

PropertyFinderApp 繼承了 React.Component(React UI的基礎模組)。元件包含著不可變的屬性,可變的狀態變數以及暴露給渲染用的方法。這會你做的應用比較簡單,只用一個渲染方法就可以啦。

React Native 元件並不是 UIKit 類,它們只能說是在某種程度上等同。框架只是將 React 元件樹轉化成為原生的UI。

最後一步啦,將這一行加在檔案末尾:

React.AppRegistry.registerComponent('PropertyFinder', function() { return PropertyFinderApp });

AppRegistry 定義了App的入口,並提供了根元件。

儲存 PropertyFinderApp.js,回到Xcode中。確保 PropertyFinder 規劃(scheme)已經勾選了,並設定了相應的 iPhone 模擬器,然後生成並執行你的專案。幾秒之後,你應該就可以看到 “Hello World” 應用正在運行了:

這個JavaScript應用執行在模擬器上,使用的是原生UI,沒有任何內嵌的瀏覽器哦!

還不相信這是真的?:] 那開啟你的 Xcode,選擇 Debug\View Debugging\Capture View Hierarchy,你看 native view hierarchy 中都沒有 UIWebView,就只有一個原生的view!:]

你一定很好奇其中的原理吧,那就在 Xcode 中開啟 AppDelegate.m,接著找到 application:didFinishLaunchingWithOptions:這個方法構建了 RCTRootView 用於載入 JavaScript 應用以及渲染最後的檢視的。

當應用開始執行的時候,RCTRootView將會從以下的URL中載入應用:

http://localhost:8081/index.ios.bundle

重新呼叫了你在執行這個App時開啟的終端視窗,它開啟了一個 packager 和 server 來處理上面的請求。

在 Safari 中開啟那個 URL;你將會看到這個 App 的 JavaScript 程式碼。你也可以在 React Native 框架中找到你的 “Hello World” 程式碼。

當你的App開始運行了以後,這段程式碼將會被載入進來,然後 JavaScriptCore 框架將會執行它。在 Hello World 的例子裡,它將會載入 PropertyFinderApp 元件,然後構建出原生的 UIKit 檢視。關於這部分的內容,後文裡會再詳細解釋的。


你好 JSX 的世界

你當前的應用程式會使用 React.createElement 來構建應用 UI ,React會將其轉換到原生環境中。在當前情況下,你的JavaScript程式碼是完全可讀的,但一個更復雜的 UI 與巢狀的元素將迅速使程式碼變成一大坨。

確保應用程式仍在執行,然後回到你的文字編輯器中,編輯 PropertyFinderApp.js 。修改元件 render 方法的返回語句如下:

return <React.Text style={styles.text}>Hello World (Again)</React.Text>;

這是 JSX ,或 JavaScript 語法擴充套件,它直接在你的 JavaScript 程式碼中混合了類似 HTML 的語法;如果你是一個 web 開發人員,應該對此不陌生。在本篇文章中你將一直使用 JSX 。

把你的改動儲存到 PropertyFinderApp.js 中,並返回到模擬器。按下 Cmd + R ,你將看到你的應用程式重新整理,並顯示更新的訊息 “Hello World(again)”。

重新執行一個 React Native 應用程式像重新整理 web 瀏覽器一樣簡單!:]

因為你會使用相同的一系列 JavaScript 檔案,您可以讓應用程式一直執行,只在更改和儲存 PropertyFinderApp.js 後重新整理即可

注意:如果你感到好奇,可以看看你的“包”在瀏覽器中,JSX被轉換成什麼。

這個 “Hello World” 已經夠大家玩耍了,是時候構建實際的應用程式了!


新增導航

我們的房產查詢應用使用標準的棧式導航,基於 UIKit 的 navigation controller。現在正是新增的時候。

在 index.ios.js 檔案中,把 PropertyFinderApp 重新命名為HelloWorld:

class HelloWorld extends React.Component {

“Hello World” 這幾個字你還需要讓它顯示一會兒,但它不再是應用的根元件了。

接下來,在 HelloWorld 這個元件下面新增如下這個類:

class PropertyFinderApp extends React.Component {
  render() {
    return (
      <React.NavigatorIOS
        style={styles.container}
        initialRoute={{
          title: 'Property Finder',
          component: HelloWorld,
        }}/>
    );
  }
}

構造一個 navigation controller,應用一個樣式,並把初始路由設為 Hello World 元件。在 Web 開發中,路由就是一種定義應用導航的一種技術,即定義頁面——或者說是路由——與 URL 的對應關係。

在同一個檔案中,更新樣式定義,包含如下 container 的樣式:

var styles = React.StyleSheet.create({
  text: {
    color: 'black',
    backgroundColor: 'white',
    fontSize: 30,
    margin: 80
  },
  container: {
    flex: 1
  }
});

在隨後的教程中會告訴你 flex: 1 是什麼意思。

回到模擬器,Cmd+R,看看新 UI 的樣子:

這就是包含了 root view 的 navigation controller,目前 root view 就是 “Hello World”。很棒——應用已經有了基礎的導航結構,到新增真實 UI 的時候了。

建立搜尋頁

在專案中新增一個新檔案,命名為 SearchPage.js,然後將其放在PropertyFinderApp.js 所在目錄下。在檔案中新增下面程式碼:

'use strict';

var React = require('react-native');
var {
  StyleSheet,
  Text,
  TextInput,
  View,
  TouchableHighlight,
  ActivityIndicatorIOS,
  Image,
  Component
} = React;

你會注意到,位於引入 react-native 所在位置的前面有一個嚴格模式標識,緊接著的宣告語句是新知識。

這是一種解構賦值,准許你獲取物件的多個屬性並且使用一條語句將它們賦給多個變數。結果是,後面的程式碼中可以省略掉 React 字首;比如,你可以直接引用 StyleSheet ,而不再需要 React.StyleSheet。解構同樣適用於運算元組,更多細節請戳這裡

繼續在 SearchPage.js 檔案中新增下面的樣式:

var styles = StyleSheet.create({
  description: {
    marginBottom: 20,
    fontSize: 18,
    textAlign: 'center',
    color: '#656565'
  },
  container: {
    padding: 30,
    marginTop: 65,
    alignItems: 'center'
  }
});

同樣,以上都是標準的 CSS 屬性。和 Interface Builder 相比,這樣設定樣式缺少了視覺化,但是比起在 viewDidLoad() 中逐個設定檢視屬性的做法更友好!

只需要把元件新增到樣式宣告的前面:

class SearchPage extends Component {
  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.description}>
          Search for houses to buy!
        </Text>
        <Text style={styles.description}>
          Search by place-name, postcode or search near your location.
        </Text>
      </View>
    );
  }
}

render 很好地展示出 JSX 以及它表示的結構。通過這個樣式,你可以輕易地描繪出元件 UI 的結構:一個容器,包含兩個 text 標籤。

最後,將下面的程式碼新增到檔案末尾:

module.exports = SearchPage;

這可以 export SearchPage 類,方便在其他檔案中使用它。

下一步是更新應用的路由,以初始化路由。

開啟 PropertyFinderApp.js,在檔案頂部緊接著上一個 require 語句的位置新增下面程式碼:

var SearchPage = require('./SearchPage');

在 PropertyFinderApp 類的 render 函式內部,通過更新 initialRoute 來引用最新新增的頁面,如下:

component: SearchPage

此時,如果你願意則可以移除 HelloWorld 類以及與它相關聯的樣式。你不在需要那段程式碼了。

切換到模擬器,按下 Cmd+R 檢視新的 UI:

使用 Flexbox 定義外觀

現在,你已經看到了用基本的 CSS 屬性來控制外間距(margin),內間距(padding)還有顏色(color)。不過,可能你還不太瞭解要如何使用伸縮盒(flexbox),flexbox 是最近新加入 CSS 規範,用它就能很便利地佈局介面。

React Native 用 css-layout(這是一個用 JavaScript 實現flexbox標準然後編譯成 C(iOS平臺)或者Java(Android平臺)的庫)。

Facebook把這個專案單獨出來實在太正確了,這樣可以編譯成多種語言,促進更多新穎的應用的發展,比如flexbox layout to SVG

在你的App中,容器(container)預設地是縱向佈局,也就是說在它的子元素將會豎直地排列,像這樣:

這被稱為主軸 (main axis),它的方向可以是豎直的也可以是水平的。

每一個子元素在豎直方向上的位置是由它的margin,height和padding共同決定的。容器的 alignItems 屬性也要設定成 center,這個屬性可以控制子元素在十字軸上的位置。在這裡,它實現了居中對齊的文字。

好啦,現在我們把輸入框和按鈕加上去吧。開啟 SearchPage.js,將下面的程式碼插入第二個 Text 元素的後面:

<View style={styles.flowRight}>
  <TextInput
    style={styles.searchInput}
    placeholder='Search via name or postcode'/>
  <TouchableHighlight style={styles.button}
      underlayColor='#99d9f4'>
    <Text style={styles.buttonText}>Go</Text>
  </TouchableHighlight>
</View>
<TouchableHighlight style={styles.button}
    underlayColor='#99d9f4'>
  <Text style={styles.buttonText}>Location</Text>
</TouchableHighlight>

現在你已經加上了兩個最高等級的檢視(top-level view),一個檢視包含了文字輸入框和一個按鈕,還有一個檢視內只有一個按鈕。在後文中你會看到,它們的樣式是什麼樣的。

接著,新增上對應的樣式:

flowRight: {
  flexDirection: 'row',
  alignItems: 'center',
  alignSelf: 'stretch'
},
buttonText: {
  fontSize: 18,
  color: 'white',
  alignSelf: 'center'
},
button: {
  height: 36,
  flex: 1,
  flexDirection: 'row',
  backgroundColor: '#48BBEC',
  borderColor: '#48BBEC',
  borderWidth: 1,
  borderRadius: 8,
  marginBottom: 10,
  alignSelf: 'stretch',
  justifyContent: 'center'
},
searchInput: {
  height: 36,
  padding: 4,
  marginRight: 5,
  flex: 4,
  fontSize: 18,
  borderWidth: 1,
  borderColor: '#48BBEC',
  borderRadius: 8,
  color: '#48BBEC'
}

要注意格式問題:每一個樣式都是用逗號分隔開的,所以別忘了在container 選擇器後面還要加上一個逗號。

以上的樣式將會應用在你剛剛加上的輸入框和按鈕上。

現在返回到模擬器,然後按下 Cmd+R 重新整理介面:

文字區域和 ’Go’ 按鈕在同一行,不需要顯式地定義兩個元件的寬度,你只需要將它們放在同一個容器中,加上 flexDirection:'row' 樣式,再定義好它們的 flex 值。文字區域是 flex:4,按鈕則是 flex:1,這說明兩者的寬度比是4:1。

大概你也發現了,你的“按鈕”其實並不是按鈕!:] 使用了 UIKit 後,按鈕更傾向於是可以輕碰(tap)的標籤(label),所以 React Native 團隊決定直接在 JavaScript 中構建按鈕了。所以你在 App 中使用的按鈕是 TouchableHighlight,這是一個 React Native 元件,當輕碰 TouchableHighlight 時,它會變得透明從而顯示出襯底的顏色(也就是按鈕下層的元件顏色)。

搜尋介面的最後一步就是加上一張圖片.你可以從這裡下載我們用的圖片素材並解壓。

在Xcode中開啟Images.xcassets檔案,點選加號新增一個新的圖片集。然後將圖片素材拖進正確的“區間”:

你需要重啟應用才能讓圖片生效。

將以下程式碼新增到 TouchableHighlight 元件後面,它將用於“獲取位置”按鈕:

<Image source={require('image!house')} style={styles.image}/>

現在再樣式表的最後加上圖片對應的樣式,別忘了給原樣式中最後一個加上逗號哦:

image: {
  width: 217,
  height: 138
}

require('image!house') 語句用於確定在你應用的asset目錄下的圖片資源,在 Xcode 中,如果你的打開了 Images.xcassets,你會看到一個“房屋”的圖示,正是上面程式碼中引用到的。

返回到模擬器,Cmd+R重新整理UI:

注意:如果你這會沒有看到“房屋”圖片,取而代之的是一張“找不到資源”的圖片,嘗試重啟packager(也就是在終端裡輸入 npm start 命令)。

現在你的應用看起來挺不錯的啦,不過它還少了點功能。接下來你的任務就是給它加上點狀態,讓它執行一些操作。


新增元件狀態

每個 React 元件都帶有一個key-value儲存的狀態物件,你必須在元件渲染之前設定其初始狀態。

在 SearchPage.js 中,我們對 SearchPage 類中,render方法前新增以下的程式碼。

constructor(props) {
  super(props);
  this.state = {
    searchString: 'london'
  };
}

現在你的元件擁有一個狀態變數:searchString ,且初始值被設定為 london 。

這時候你需要利用起元件中的狀態了。在render方法中,用以下的程式碼替換TextInput元素中的內容:

<TextInput
  style={styles.searchInput}
  value={this.state.searchString}
  placeholder='Search via name or postcode'/>

這一步設定了 TextInput 元件 value 屬性的值,這個值用於把狀態變數 searchString 的當前值作為展示給使用者的文字。我們已經考慮初始值的設定了,但如果使用者編輯這裡的文字會發生什麼呢?

第一步需要建立一個方法來處理事件。在 SearchPage 類中新增以下的程式碼:

onSearchTextChanged(event) {
  console.log('onSearchTextChanged');
  this.setState({ searchString: event.nativeEvent.text });
  console.log(this.state.searchString);
}

上面的程式碼從 events 中取出了 text 屬性的值,並用於更新元件的狀態。這裡面也添加了一些有用的除錯程式碼。

當文字改變時,需要讓這個方法被呼叫,呼叫後的文字會通過 render 函式返回到元件中。因此我們需要在標籤上新增一個 onChange 屬性,新增後的標籤如下所示:

<TextInput
  style={styles.searchInput}
  value={this.state.searchString}
  onChange={this.onSearchTextChanged.bind(this)}
  placeholder='Search via name or postcode'/>

當用戶更改文字時,會呼叫 onChange 上 的函式;在本例中,則是 onSearchTextChanged 。

注意:你估計會對 bind(this) 語句有疑問。在 JavaScript 中,this 這個關鍵字有點不同於大多數其他語言;在 Swift 表示 “自身”。在這種情況中,bind 可以確保在 onSearchTextChanged 方法中, this 可以作為元件例項的引用。有關更多資訊,請參見MDN this頁面。

在你再次重新整理你的應用程式之前,還有一個步驟:在 return 前新增以下語句,列印一條日誌來記錄 render() 函式的呼叫:

 console.log('SearchPage.render');

你會從這些日誌語句中學到一些很有趣的東西!:]

回到你的模擬器,然後按Cmd + R。您現在應該看到文字輸入的初始值為 “london” ,編輯一下文字,從而在 Xcode 控制檯中產生一些日誌:

注意看上面的截圖,日誌列印的順序看起來有些奇怪:

第一次呼叫 render() 函式用於設定檢視。當文字變化時, onSearchTextChanged 函式被呼叫。之後,通過更新元件的狀態來反映輸入了新的文字,這會觸發另一次 render 。 onSearchTextChanged() 函式也會被呼叫,會將改變的字串打印出來。每當應用程式更新任何 React 元件,將會觸發整個UI層的重新繪製,這會呼叫你所有元件的 render 方法。這是一個好主意,因為這樣做把元件的渲染邏輯,從狀態變化影響UI這一過程中完全解耦出來。

與其他大多數 UI 框架所不同的是,你既不需要在狀態改變的時候去手動更新 UI ,或使用某種型別的繫結框架,來建立某種應用程式狀態和它的 UI 表現的關聯;例如,我的文章中講的,通過ReactiveCocoa實現MVVM模式

在 React 中,你不再需要擔心 UI 的哪些部分可能受到狀態變化的影響;你的整個應用程式的 UI,都可以簡單地表示為一個函式的狀態。

此時,你可能已經發現了這一概念中一個根本性的缺陷。是的,非常準確——效能!

你肯定不能在 UI 變化時,完全拋棄掉整個 UI 然後重新繪製吧
?這就是 React 高明的地方了。每當 UI 渲染出來後,render 方法會返回一顆檢視渲染樹,並與當前的 UIKit 檢視進行比較。這個稱之為 reconciliation 的過程的輸出是一個簡單的更新列表, React 會將這個列表應用到當前檢視。這意味著,只有實際改變了的部分才會重新繪製。

這個令人拍案叫絕的嶄新概念讓ReactJS變得獨特——virtual-DOM(文件物件模型,一個web文件的檢視樹)和 reconciliation 這些概念——被應用於iOS應用程式。

稍後你可以整理下思路,之後,在剛才的應用中你仍然有一些工作要做。日誌程式碼增加了程式碼的繁瑣性,已經不需要了,所以刪除掉日誌程式碼。


初始化搜尋功能

為了實現搜尋功能,你需要處理 “Go” 按鈕的點選事件,呼叫對應的 API,並提供一個視覺效果,告訴使用者正在做查詢。

在 SearchPage.js 中,在建構函式中把初始狀態更新成:

this.state = {
  searchString: 'london',
  isLoading: false
};

新的 isLoading 屬性將會記錄是否有請求正在處理的狀態。

在 render 開始的新增如下邏輯:

var spinner = this.state.isLoading ?
  ( <ActivityIndicatorIOS
      hidden='true'
      size='large'/> ) :
  ( <View/>);

這是一個三元操作符,與 if 語句類似,即根據元件 isLoading 的狀態,要麼新增一個 indicator,要麼新增一個空的 view。因為整個元件會不停地更新,所以你自由地混合 JSX 和 JavaSript 程式碼。

回到用 JSX 定義搜尋介面的地方,在圖片的下面新增:

{spinner}

給渲染“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 設定為對應的值,這樣 UI 就可以顯示新的狀態了。

提示: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,因此被定義成了一個獨立的函式,而不是類方法。他首先通過 data 來定義查詢字串所需要的引數,接著將 data 轉換成需要的字串格式,name=value 對,使用 & 符號分割。語法 => 是一個箭頭函式,又一個對 JavaScript 語言的擴充套件,提供了這個便捷的語法來建立一個匿名函式。

回到模擬器,Cmd+R,重新載入應用,點選 “Go” 按鈕。你可以看到 activity indicator 顯示出來,再看看 Xcode 的控制檯:

activity indicator 渲染了,並且作為請求的 URL 出現在輸出中。把 URL 拷貝到瀏覽器中訪問看看得到的結果。你會看到大量的 JSON 物件。別擔心——你不需要理解它們,之後會使用程式碼來解析之。

提示:應用使用了 Nestoria 的 API 來做房產的搜尋。API 返回的 JSON 資料非常的直白。但是你也可以看看文件瞭解更多細節,請求什麼 URL 地址,以及返回資料的格式。

下一步就是從應用中發出請求。


執行 API 請求

還是 SearchPage.js 檔案中,更新構造器中的初始 state 新增一個message 變數:

this.state = {
  searchString: 'london',
  isLoading: false,
  message: ''
};

在 render 內部,將下面的程式碼新增到 UI 的底部:

<Text style={styles.description}>{this.state.message}</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 函式,它是 Web API 的一部分。和 XMLHttpRequest 相比,它提供了更加先進的 API。非同步響應會返回一個 promise,成功的話會轉化 JSON 並且為它提供了一個你將要新增的方法。

最後一步是將下面的函式新增到 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.'});
  }
}

如果查詢成功,這個方法會清除掉正在載入標識並且記錄下查詢到屬性的個數。

注意:Nestoria 有很多種返回碼具備潛在的用途。比如,202 和 200 會返回最佳位置列表。當你建立完一個應用,為什麼不處理一下這些,可以為使用者呈現一個可選列表。

儲存專案,然後在模擬器中按下 Cmd+R,嘗試搜尋 ‘london’;你會在日誌資訊中看到 20 properties were found。然後隨便嘗試搜尋一個不存在的位置,比如 ‘narnia’,你會得到下面的問候語。

是時候看一下這20個房屋所對應的真實的地方,比如倫敦!

結果顯示

建立一個新的檔案,命名為 SearchResults.js,然後加上下面這段程式碼:

'use strict';

var React = require('react-native');
var {
  StyleSheet,
  Image, 
  View,
  TouchableHighlight,
  ListView,
  
            
           

相關推薦

深入淺出 React Native使用 JavaScript 構建原生應用

基於 PhoneGap 使用 JavaScript 和 HTML5 開發 iOS 應用已經有好幾年了,那 React Native 有什麼牛的? React Native 真的很牛,讓大家興奮異常的主要原因有兩點: 可以基於 React Native使用 JavaScript 編寫應用邏輯,UI 則可

React Native嵌入到現有原生應用

如果你正準備從頭開始製作一個新的應用,那麼React Native會是個非常好的選擇。但如果你只想給現有的原生應用中新增一兩個檢視或是業務流程,React Native也同樣不在話下。只需簡單幾步,你就可以給原有應用加上新的基於React Native的特性、畫面和檢視等。

react native 學習筆記----將react native嵌入到Android原生應用

不僅可以在react native 的js介面和現有工程的介面之間跳轉,而且可以把js寫的介面當成一個控制元件,嵌入到現有的activity,作為原生介面的一部分使用。 第一節:按照官方的例子,把js寫頁面放在一個activity,在原生應用裡啟動該activity。 開

React Native在特贊的應用與實踐

使用 anywhere node.js 交互操作 網關 初始 andro -- 所有 基於React技術棧構建開發前端項目,並使用React Native開發特贊移動APP 目前正在使用Node.js開發和維護特贊服務網關,希望Node.js能夠在更輕量級的微服務架構中發

react-native-router-flux(簡單應用

lex app turn rom pos dir ber round ans 安裝 創建項目;並且安裝上 react-native-router-flux 包 react-native init ReactNativeRouterFluxDemo cd React

react-native 在匯入android原生庫容易遇到的問題

在rn開發中,如果遇到了比較特殊的功能,如果沒有現成的輪子的話,通常我們需要去連結原生庫,並且封裝成RN元件提供給js呼叫,作為前端開發者,通常我們對原生端的報錯處理不太懂,在處理和原生功能相關問題時,推薦直接使用android-studio進行開發(專案名->android->app->

(五)react-native開發系列之原生互動

react-native可以做web與原生的互動,這是使用react-native開發專案的主要目的之一,也是主要優勢,用rn而不用原生互動則毫無價值,這篇文章用來記錄在專案中rn的原生互動使用過程。 之前說過要做的是一個pda專案,所以今天以input獲取焦點的時候禁止軟鍵盤彈出為例,大體說一下rn的原生

React native(StatusBar)修改狀態列背景及文字顏色

  在自定義導航欄得時候,狀態列得背景顏色和狀態列得顏色是不統一得,看起來很不協調,RN中文網找到了StatusBar,可以設定狀態列。https://reactnative.cn/docs/statusbar.html#docsNav 首先我定義了一些屬性得約束,狀態列只用到

React Native自定義一個導航欄,改變狀態列背景,隱藏狀態列

設計開發過程中,導航欄都會有所不同,這時候使用RN就需要自定義一個想要的導航欄了,RN中文網有講專門ios的導航欄(NavigatorIOS),可以不用自定義。 首先定義自定義導航欄的一些屬性的約束,記得npm install --save prop-types然後引入import Prop

react-native整合到現有原生專案

必須具備react-native的開發環境 建立結構目錄 建立一個名為rn_test的資料夾(這個資料夾是存放react-native專案的),在rn_test資料夾中建立名為android的資料夾(是存放android專案的)把原生專案複製進去,這裡我建立了一個新的 引入rea

如何用 React Native 開發實時音視訊應用

對於 Web、iOS、Android 開發者來講,React Native 給跨平臺開發工作帶來了很大的幫助。僅用 JavaScript 就可以建立運行於移動端的應用。同時,你也可以將 React Native 程式碼與 Native 程式碼結合,不論你是用 Objective C、Java 還是用 Sw

關於react-native-code-push的原生整合攻略

注:此文整合code-push是基於自建熱更新伺服器來維護的,如果使用微軟的熱更新伺服器整合有部分區別。 首先通過npm 或者 yarn 將依賴下載下來: npm install --save react-native-code-push  or yarn add r

基於React Native的跨三端應用架構實踐

作者|陳子涵 編輯|覃雲 “一次編寫, 到處執行”(Write once, run anywhere ) 是很多前端團隊孜孜以求的目標。實現這個目標,不但能以最快的速度,將應用推廣到各個渠道,而且還能節省大量人力物力。 React Native 的推出,為跨平臺的開發帶來了新的曙光。 雖然 Facebo

React Native之ViewPagerAndroid講解與應用

   做過移動開發的朋友都知道,ViewPager控制元件在應用場景是在常見不過了,React Native(RN)給我們提供了一套針對安卓平臺的component:ViewPagerAndroid。不過github上面有人實現了跨平臺的ViewPager,只是體驗上有點欠

React-Native與安卓原生的混合開發

寫在前面 目前很多大廠APP(如淘寶、餓了麼、美團等等)並不是純原生Android&IOS,也不是純JS開發,而是Hybird APP開發,混合型優勢很多:比如熱更新,保證在一些類似雙十一的活動到來時能夠快速上線活動頁面,使用者不必再去更新APP。再來有效地減小了安裝包的體積

React Native整合到IOS原生專案

這裡預設電腦上已經安裝了cocoapods和React-Native,如果沒有RN開發環境,可以點選這裡按照步驟配置。 0、新建專案 首先,先使用xcode新建一個專案,然後在專案的根目錄下新建一個資料夾,用於存放RN的元件庫還有其他一些檔案。這

(八)React Native實現呼叫android原生java方法並實現廣播的傳送和接受

接觸到混合應用開發提議後,首先想到了之前學的ionic,其次便是這兩年火遍全球的RN,由於ionic框架用的angular.js,而且angular1和angular2寫法區別很大,angular2對於初學者還是比較好接受的,但是angular1寫的很亂,程式碼

React Native 與 嵌入Android原生與Activity頁面互相跳轉

前言    RN作為混合開發,肯定需要與原生直接的頁面跳轉,這裡也屬於和原生端通訊的知識模組。我們知道Android的頁面跳轉是通過Intent、Rn是通過路由,而兩者直接頁面互相跳轉就需要原生藉助JS

React native 介面跳轉原生Android介面

最近在學習React native,正好看到RN介面跳轉原生的介面,但是根據網上的來 總是會報undefined is not an object (evaluating ‘NativeModules.IntentModule.startActivityFrom

React-Native開發總結+構建-除錯-基礎歸納總結

更新時間:2017年11月07日16:47:07    對於前端工程師來說,這幾年是最輝煌的時刻,前端框架層出不窮,而且出現了像RN這種功能的框架,更讓移動開發工程師大跌眼鏡望洋興嘆。新技術的不斷湧現,說明了一個問題,前端在程式設計技術界的地位越來越高,而且前端工程師的崗位越