React Nativeを使用してJavaScriptアプリケーションを構築する

このレッスンでは、iOSおよびAndroid用のネイティブアプリケーションを作成するためのFacebookのフレームワークであるReact Nativeについて学習します。 他の非常に人気のあるFacebookフレームワークであるReact Javascriptと多くの共通点があります。これは、宣言的なユーザーインターフェイスを構築するために設計されています。




注:これはiOSチームのColin Eberhardによって書かれたレッスンの更新版で、React Native 0.22の多くの編集が含まれています。

ただし、現時点では、 PhoneGapTitaniumなど、iOSアプリケーションを作成するためにJavaScriptを使用する十分なフレームワークがあります。 React Nativeが特別な理由は何ですか?

1. PhoneGapとは異なり、React Nativeでは、アプリケーションロジックはJavaScriptで記述され実行されますが、そのインターフェイスは完全にネイティブのままです。 したがって、HTML5 UIに固有の妥協は必要ありません。
2. Titaniumとは異なり、Reactは、ユーザーインターフェイスを作成するための新しい独創的で非常に効果的なアプローチを導入します。 つまり、アプリケーションUIは、アプリケーションの現在の状態の関数として表されます。

React Nativeの重要な機能は、その開発者がモバイルアプリケーション開発業界にReactプログラミングモデルをもたらすことを意図していることです。 重要な明確化:これは、ソフトウェアを一度書いてどこでも使用できるようなクロスプラットフォームのツールではなく、一度勉強してどこにでも書くことができるものについてです。 このレッスンはiOSプラットフォームを対象としていますが、提示されたすべての資料を学習したので、Androidアプリケーションを簡単に作成できます。

Objective-CまたはSwiftでアプリケーションを作成した経験がある場合は、おそらくJavaScriptに切り替えるというアイデアに満足できないでしょう。 しかし同時に、2番目のポイントには明らかに興味のあるSwift開発者がいるはずです。

間違いなく、Swiftを使用するときは、アルゴリズムを暗号化するための多くの新しい効果的な方法と、変換と不変性を促進する手法を学ぶ必要がありました。 ただし、ここでUIを作成する方法は、Objective-Cで作業するときに使用する方法と非常に似ています。これもUIKitに基づいており、必須です。

Reactは、Virtual DOMやリコンシリエーションなどの珍しい概念を通じて、機能的なプログラミングをユーザーインターフェイスレイヤーにもたらします。

このReact Nativeチュートリアルでは、英国のプロパティ検索アプリケーションを作成します。



JavaScriptを使用したことがない場合でも心配する必要はありません。 各開発ステップを詳細に分析します。 Reactは、CSSに似たスタイリングの構文を使用しており、読みやすく理解しやすいですが、その場合は常にMozilla Developer Networkを参照できます。

面白い? どうぞ

仕事を始める

React NativeはJavaScriptランタイムであるNode.jsを使用してJavaScriptコードを作成します。 Node.jsをまだインストールしていない場合は、今度はインストールします。

まず、サイトの指示に従ってHomebrewをインストールし、次にターミナルウィンドウで以下を実行してNode.jsをインストールします。

brew install node 

次に、 homebrewを使用して変更を追跡し、Facebookからファイルを検索するサービスであるwatchmanをインストールします。

 brew install watchman 

React Nativeはこれを使用してコードの変更を追跡し、適切な編集を行います。 これはXcodeのようなものですが、ファイルが保存されるたびにビルドされます。

次に、npmを使用してReact Nativeコマンドラインインターフェイス(CLI)をインストールします。

 npm install -g react-native-cli 

Node Package Managerを使用して、CLIツールを呼び出してグローバルにインストールします。 npmにはNode.jsが付属しており、その機能はCocoaPodsまたはCarthageに似ています。

React Nativeをより深く理解したい人のために、そのソースコードはGitHubで公開されています

プロジェクトを保存するフォルダーを参照し、CLIツールを使用して作成します。

 react-native init PropertyFinder 

この行は、React Nativeでアプリケーションを開発および実行するために必要なすべてを含む初期プロジェクトを作成します。

Node.jsの古いバージョンに関する通知が表示された場合は、brewによってインストールされたものが最新のものであることを確認してください。 これを行うには、ターミナルでbrew link --overwrite nodeコマンドを実行します

作成されたフォルダーとファイルを見ると、React Nativeフレームワークが配置されているnode_modulesフォルダーがあります。 index.ios.jsファイルは、CLIツールによって作成されたアプリケーションレイアウトです。 また、iosフォルダーにも注意してください。これには、Xcodeプロジェクトと、Bootstrapと統合するための小さなコードが含まれています。 最後に、Android用のコンポーネントもありますが、ここでは考慮しません。

プロジェクトファイルを開き、ビルドして実行します。 シミュレーターは次のメッセージを表示します。



:執筆時点で、React Native CLIツールによって作成された初期プロジェクトは、ビルド中に3つの警告を発行しました。 したがって、Xcodeからの通知を最初に表示するときに心配する必要はありません。 React Native開発者はこの小さな問題を認識しており、次のReact Nativeリリースで修正するために協力しています。

また、おそらく次のメッセージで端末のポップアップに気づいたでしょう。

  ┌──────────────────────────────────────────────┐ │ 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 │ │ │ └──────────────────────────────────────────────┘ Looking for JS files in /Users/tomelliott/Desktop/Scratch/PropertyFinder [6:15:40 PM] <START> Building Dependency Graph [6:15:40 PM] <START> Crawling File System [6:15:40 PM] <START> Loading bundles layout [6:15:40 PM] <END> Loading bundles layout (0ms) [Hot Module Replacement] Server listening on /hot React packager ready. [6:15:41 PM] <END> Crawling File System (747ms) [6:15:41 PM] <START> Building in-memory fs for JavaScript [6:15:42 PM] <END> Building in-memory fs for JavaScript (653ms) [6:15:42 PM] <START> Building in-memory fs for Assets [6:15:42 PM] <END> Building in-memory fs for Assets (277ms) [6:15:42 PM] <START> Building Haste Map [6:15:42 PM] <START> Building (deprecated) Asset Map [6:15:42 PM] <END> Building (deprecated) Asset Map (49ms) [6:15:42 PM] <END> Building Haste Map (400ms) [6:15:42 PM] <END> Building Dependency Graph (2094ms) 

これは、Node.jsを実行するReact Nativeラッパーです。 すぐにあなたはそれが何であるかを知るでしょう。

ターミナルウィンドウを閉じないで、バックグラウンドで動作させます。 誤って閉じてしまった場合は、Xcodeを使用してプロジェクトを停止して再起動してください。

:コードのジャングルに入る前に、テキストエディターの選択を決定する必要があります。 多くのJavaScriptコードを記述する必要があり、Xcodeは明らかにこれに適していない。 Sublime Textを使用していますが、これは安価で非常に便利なツールです。 しかし、 AtomBrackets、またはその他の軽量エディターも素晴らしいです。

Hello React Native

不動産検索アプリケーションの作業を開始する前に、Hello Worldアプリケーションを作成します!..プロセスでは、新しいコンポーネントと概念を紹介します。

index.ios.jsファイルをテキストエディターで開き、そのコンテンツをすべて削除します。アプリケーションを最初から作成します。 ファイルの先頭に次を追加します。

 'use strict'; 

このディレクティブは、 厳格なモードを宣言します 。これにより、エラー処理が改善され、一部のJavaScript要素に制限が課されます。 簡単に言えば、JavaScriptのパフォーマンスが向上します。

:ストリクトモードの詳細については、John RezigのECMAScript 5 Strict Mode、JSONなどの記事を参照してください。

次に、次の行を追加します。

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

react-nativeモジュールをロードし、 React変数に割り当てます。 React Nativeは、 require関数を備えたNode.jsと同じモジュールローディングテクノロジーを使用します。これは、Swiftにライブラリを接続してインポートするのとほぼ同等です。

:JavaScriptモジュールの詳細については、Eddie OsmaniのモジュラーJavaScriptに関する記事を参照してください

次に、次を追加します。

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

このコードは、Hello World!のテキストにすぐに適用される単一のスタイルを定義します。すでにWeb開発の経験がある場合は、おそらくこれらのプロパティを認識しています。 インターフェイスのスタイル設定に使用されるStyleSheetクラスの外観は、Webで広く使用されているカスケードスタイルシート(CSS)の構文に似ています。

それでは、アプリケーションに直接対処しましょう。 スタイルシート変数の直下に次のコードを追加します。

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

はい、これはJavaScriptクラスです。

クラスがECMAScript 6(ES6)に追加されました。 JavaScriptは常に進化しているため、開発者は古いシステムやブラウザーとの互換性を維持するために使用される手段を制限する必要があります。 iOS 9はES6を完全にはサポートしていませんが、React NativeはBabelというツールを使用します。このツールは、必要に応じて、最新バージョンのJavaScriptを古いバージョンのJavaScriptと互換性のあるものに自動的に変換します。

:Web開発者であれば、ブラウザでBabelを使用することもできます。 そのため、古いバージョンのブラウザをサポートする場合でも、古いバージョンのJavaScriptを使用する理由は本当にありません。

PropertyFinderAppは、ReactインターフェースのコアビルディングブロックであるReact.Componentを拡張します。 コンポーネントには不変のプロパティと可変の状態変数が含まれています。 それらはレンダリングの方法を提供します。 現在作業中のアプリケーションは非常にシンプルで、レンダリングメソッドのみが必要です。

React NativeコンポーネントはUIKitクラスではなく、軽量の同等コンポーネントです。 フレームワークは、Reactコンポーネントツリーを必要なネイティブインターフェイスに変換します。

最後に、この行をファイルの最後に追加します。

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

AppRegistryは、アプリケーションへのエントリポイントを定義し、ルートコンポーネントを提供します。

index.ios.jsへの変更を保存し、Xcodeに戻ります。 PropertyFinderスキーマがiPhoneシミュレーターの1つで選択されていることを確認してから、プロジェクトをビルドして実行します。 数秒後、Hello World!アプリケーションが画面に表示されます。



これは、ネイティブUIを表示するシミュレーターで実行されるJavaScriptアプリケーションであり、ブラウザーはありません。

まだ信じられない? 自分の目で確かめてください:Xcode Debug \ View Debugging \ Capture View Hierarchyを選択すると、ネイティブビュー階層が表示されます。 また、全体にUIWebViewエンティティがあります。 アプリケーションテストがRCTTextに表示されます 。 しかし、それは何ですか? Xcodeに戻り、[ ファイル] 、[ すばやく開く]の順に選択し、 RCTView.hと入力します。 RCTViewはUIViewから直接継承することに注意してください すべてが正常に機能することがわかりました。



仕組みを知りたいですか? XcodeのAppDelegate.mを開きアプリケーションを見つけます:didFinishLaunchingWithOptions:。 このメソッドは、JavaScriptアプリケーションをロードし、結果のビューをレンダリングするRCTRootViewを作成します。

アプリケーションが起動すると、RCTRootViewは次のURLからアプリケーションをダウンロードします。

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

このアプリケーションを起動したときに開いていたターミナルウィンドウを思い出してください。 上記のリクエストを処理するパッカーとサーバーを起動します。

SafariでこのURLを開くと、アプリケーションのJavaScriptコードが表示されます。 また、React Nativeフレームワークに組み込まれているHello World!コードも確認する必要があります。

アプリケーションが起動すると、このコードが読み込まれ、JavaScriptCoreフレームワークによって実行されます。 この場合、 PropertyFinderAppコンポーネントをロードしてから、ネイティブUIKitビューを構築します。 レッスンの後半で、これについてさらに詳しく説明します。

Hello World JSX

作成されたアプリケーションは、 React.createElementを使用して、Reactを使用してネイティブの同等のものに変換できるシンプルなインターフェイスを構築します。 また、現在のJavaScriptコードは読みやすいですが、要素が埋め込まれたより複雑なUIの場合は、混乱する可能性があります。

アプリケーションがまだ実行中であることを確認してから、 index.ios.jsファイルの編集に戻り 、次のようにreturnステートメントを変更します。

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

これは、HTMLのような構文をJavaScriptコードに追加するJavaScript構文拡張機能であるJSXです。 すでにWeb開発の経験がある人は、後者に似ていることに気付くでしょう。 レッスン全体でJSXを使用します。

index.ios.jsへの変更を保存して、シミュレーターに戻ります。 Cmd + Rを押して、画面上のメッセージを更新します。



React Nativeでアプリケーションを再起動するのは、ブラウザーページを更新するのと同じくらい簡単です。 この場合、JavaScriptファイルに関する変更のみが表示されることに注意してください。 それ以外の場合はすべて、Xcodeでアプリケーションを再構築する必要があります。

このチュートリアルでは同じJavaScriptコンポーネントのセットを使用するため、 index.ios.jsへの変更を保存した後、アプリケーションをそのまま動作させて更新することができます。

:JSXの変換先に興味がある場合は、ブラウザーの「バンドル」をご覧ください。

Hello Worldで十分にプレイできたと思うので、今度は実際のアプリケーションを作成します。

ナビゲーションを追加

Property Finderアプリケーションは、UIKitナビゲーションコントローラーが提供する標準スタックナビゲーションを使用します。 この動作を追加します。

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を作成し、スタイルを適用して、HelloWorldコンポーネントへの初期ルートを設定します。 Web開発では、 ルーティングはアプリケーションのナビゲーション構造を決定する方法であり、ページまたはルートは対応するURLにリンクされます。

次に、以下に示すように、コンテナパラメータを追加してスタイルを調整します。

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

flex:1が何であるかについて 、少し後で学習します。

変更を保存し、シミュレーターに戻り、Cmd + Rを押して、更新されたインターフェースを表示します。



Navigation Controllerのルート表現は、Hello World!..というテキストに対応しています。これで、現在のアプリケーションの基本的なナビゲーション構造ができました。 実際のUIを追加します。

検索ページを作成する

SearchPage.jsという新しいファイルをプロジェクトに追加し、 index.ios.jsと同じフォルダーに配置します。 このファイルを新しいファイルに追加します。

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

すでに厳格モードとリアクティブネイティブでのインポートについて説明しましたが、次の割り当てステートメントは別のものです。

これは破壊的な割り当てであり、オブジェクトの多くのプロパティを抽出し、それらを単一のステートメントで変数に割り当てることができます。 その結果、残りのコードにReactプレフィックスをドロップできます。 たとえば、 React.StyleSheetではなく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()メソッドで一度に1つのビュープロパティを設定するよりも、この方法の方が間違いなく優れています。

コンポーネント自体をスタイルの下に直接挿入します。

 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とその構造を完全に実証します 。 スタイルとともに、このコンポーネントによって作成されたインターフェース(2つのテキストラベルを持つコンテナー)を非常に簡単に視覚化できます。

最後に、ファイルの最後に次の行を追加します。

 module.exports = SearchPage; 

SearchPageクラスをエクスポートし、他のファイルで使用できるようにします。

次のステップは、アプリケーションのルーティングを更新して、異なる初期ルートを確立することです。

index.ios.jsを開き、ファイルの先頭でrequireの直後に次の行を追加します。

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

以下に示すように、 PropertyFinderAppクラスのレンダリング関数で、 initialRouteを更新して、作成したページをバインドします。

 component: SearchPage 

必要に応じて、 HelloWorldクラスとそのスタイルを削除できます。 それらはもう必要ありません。
変更を保存し、シミュレーターに戻り、Cmd + Rを押して、更新されたインターフェースを表示します。



これは、新しいSearchPageコンポーネントを使用します。

Flexboxでのスタイリング

このチュートリアルでは、内側と外側の余白だけでなく、色のオプションを指定するいくつかの基本的なCSSプロパティをすでに扱いました。 ただし、おそらくflexboxについてはまだ聞いたことがないでしょう。 この技術は最近CSS仕様に追加されたばかりで、ユーザーインターフェイスレイアウトの構築に非常に役立ちます。

React Nativeはcss-layoutライブラリを使用します。これは、C(iOS用)およびJava(Android用)でコンパイルされたflexbox標準のJavaScript実装です。
React Nativeが複数のプログラミング言語を対象とした別個のプロジェクトとして作成されたことは非常に良いことです。これにより、 flexbox-layoutsをSVGに適用するなど、最新のアプローチを使用してアプリケーションを開発できます。

デフォルトでは、アプリケーションのコンテナは列の形式でデータフローの方向を持ちます。これは列パラメータに対応します。つまり、コンテナのコンテンツ全体が垂直に配置されます。



これはいわゆるメイン軸、またはメイン軸であり、水平方向と垂直方向の両方を持つことができます。

コンテナの各子要素の垂直位置は、外部および内部のインデントと高さを考慮して計算されます。 また、コンテナはalignItemsプロパティをcenterに設定し、メイン軸上の子の位置を決定します。 この場合、テキストを中央に揃えます。

次に、入力フィールドとボタンを追加します。 SearchPage.jsファイルを開き、2番目の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> 

2つのトップレベルビューを追加しました。1つには入力テキストフィールドとボタンがあり、もう1つには別のボタンがあります。 次に、これらの要素をスタイルする方法を学びます。

スタイルオプションに戻り、 コンテナブロックの後にコンマを入れて、以下に新しいスタイルを追加します。

 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' } 

書式設定に注意してください。各スタイルプロパティまたはセレクターはカンマで区切る必要があります。

これらのスタイルは、新しく追加された入力フィールドとボタン用です。

変更を保存し、シミュレーターに戻り、Cmd + Rを押して、更新されたインターフェースを表示します。



テキストフィールドと[Go]ボタンは同じ行にあります。これは、要素がflexDirection: 'row'プロパティにより行に配置されているflowRightスタイルのコンテナに配置したためです。 これらの各要素の幅をハードに設定する代わりに、 flexプロパティの値を使用して相対的な幅に設定します。 したがって、テキストフィールドsearchInputのセレクターにはflex:4があり、ボタンbutton-flex:1のセレクターには結果としてその比率は4:1です。

また、ボタンと呼ぶ要素は本質的にそうではないことにお気づきかもしれません。 実際、UIKitのボタンは単なるインタラクティブなテキストラベルです。 アプリケーションのボタンは、 TouchableHighlightというReact Nativeコンポーネントを使用します。これは、クリックすると透明になり、基になる色を表示します。

最後に、検索ページに画像を追加します。 1つのアーカイブに複数の解像度でダウンロードできます。 ダウンロード後、アーカイブを解凍します。

次に、ルートプロジェクトに「リソース」というディレクトリを作成し、3つのイメージすべてをその中に配置します。

件名ディレクトリ :ご存じのように、Appleの専門家は、可能な限り件名ディレクトリに画像を配置することを推奨しています。 ただし、React Nativeの場合これは逆に望ましくありません 。 アプリケーションのデジタルオブジェクトをコンポーネントの近くに保存すると、いくつかの利点があります。 まず、コンポーネントの独立性を維持できます。 第二に、新しいイメージを追加するのにアプリケーションをリロードする必要はありません。 そして第三に、iOSとAndroid向けのアプリケーションを開発する場合、2つのプラットフォーム用の画像を1か所に保存することができます。

SearchPage.jsファイルに戻り、 ロケーションボタンを担当するTouchableHighlightコンポーネントの終了タグの下に次の行を追加します。

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

スタイルブロックに適切な画像スタイルを追加し、前のセレクターの後にコンマを忘れずに入れます。

 image: { width: 217, height: 138 } 

変更を保存します。 シミュレーターに戻り、Cmd + Rを押して新しいインターフェースを表示します。



:家の画像が表示されず、代わりに画像が見つからなかったという通知が表示される場合は、ターミナルでnpm startコマンドを使用してパッカーを再起動してください。

私たちのアプリケーションはすでにきれいに見えますが、それでも何かが欠けています。 アプリケーションの状態を追加し、いくつかのアクションを実行する必要があります。

コンポーネントの状態を追加

Reactの各コンポーネントには、キーと値のストレージとして使用される独自の状態オブジェクトがあります。 , .

SearchPage.js SearchPage , render() :

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

state , searchString london .

. TextInput render , :

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

TextInput – – searchString . , . , ?

, , . SearchPage constructor :

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

text . , .

, TextInput 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 self . bind , this onSearchTextChanged . this MDN .

, log render() , return :

 console.log('SearchPage.render'); 

.

, Cmd+R. , 'london', Xcode - :



, , :

1. render() , .
2. onSearchTextChanged() .
3. , , render .
4. onSearchTextChanged() , .

React, , , , render . , , UI.

UI- , - , UI. , MVVM ReactiveCocoa .

React , UI , .

, , . , .

, , - . React - . , , , render, UIKit. , React . , , .

, iOS- , ReactJS : virtual-DOM ( , -) ?

, . , , .



, 'Go', API- , .

SearchPage.js , constructor :

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

isLoading , .

render :

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

if , , – isLoading . , JSX JavaScript.

, JSX, return , Image :

 {spinner} 

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 , .

: JavaScript , 'private' . , , private.

onSearchPressed() . 'Go'. , render TouchableHighlight , 'Go':

 onPress={this.onSearchPressed.bind(this)} 

, 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 , , . , . , : name=value, . => , JavaScript . .

, , Cmd+R 'Go'. . Xcode:



, URL . URL , : JSON-. , . .

: Nestoria API . JSON-, API, . , URL- .

– .

API-

SearchPage.js , , message :

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

render :

 <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 API XMLHttpRequest. promise , 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 .

: Nestoria API . , 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; 

, require , react-native .

:

 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 , , UITableView . ListView ListView.DataSource , UI .

, , . ListView , . Nestoria API guid , .

:

 module.exports = SearchResults; 

SearchPage.js , require React:

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

SearchResults SearchPage :

_handleResponse , console.log :

 this.props.navigator.push({ title: 'Results', component: SearchResults, passProps: {listings: response.listings} }); 

SearchResults API-. push- , 'Back', .

, , Cmd+R . :



- . , . .



React Native , .

SearchResults.js :

 var styles = StyleSheet.create({ thumb: { width: 80, height: 80, marginRight: 10 }, textContainer: { flex: 1 }, separator: { height: 1, backgroundColor: '#dddddd' }, price: { fontSize: 25, fontWeight: 'bold', color: '#48BBEC' }, title: { fontSize: 20, color: '#656565' }, rowContainer: { flexDirection: 'row', padding: 10 } }); 

.

renderRow() :

 renderRow(rowData, sectionID, rowID) { var price = rowData.price_formatted.split(' ')[0]; return ( <TouchableHighlight onPress={() => this.rowPressed(rowData.guid)} underlayColor='#dddddd'> <View> <View style={styles.rowContainer}> <Image style={styles.thumb} source={{ uri: rowData.img_url }} /> <View style={styles.textContainer}> <Text style={styles.price}>£{price}</Text> <Text style={styles.title} numberOfLines={1}>{rowData.title}</Text> </View> </View> <View style={styles.separator}/> </View> </TouchableHighlight> ); } 

, '300,000 GBP' GBP. , , , , . ( Image ) URL ( rowData.img_url ), React Native , .

onPress TouchableHighlight . guid .

– , :

 rowPressed(propertyGuid) { var property = this.props.listings.filter(prop => prop.guid === propertyGuid)[0]; } 

, . , , . .

, Cmd+R, :



… .

, .



PropertyView.js :

 'use strict'; var React = require('react-native'); var { StyleSheet, Image, View, Text, Component } = React; 

, .

:

 var styles = StyleSheet.create({ container: { marginTop: 65 }, heading: { backgroundColor: '#F8F8F8', }, separator: { height: 1, backgroundColor: '#DDDDDD' }, image: { width: 400, height: 300 }, price: { fontSize: 25, fontWeight: 'bold', margin: 5, color: '#48BBEC' }, title: { fontSize: 20, margin: 5, color: '#656565' }, description: { fontSize: 18, margin: 5, color: '#656565' } }); 

:

 class PropertyView extends Component { render() { var property = this.props.property; var stats = property.bedroom_number + ' bed ' + property.property_type; if (property.bathroom_number) { stats += ', ' + property.bathroom_number + ' ' + (property.bathroom_number > 1 ? 'bathrooms' : 'bathroom'); } var price = property.price_formatted.split(' ')[0]; return ( <View style={styles.container}> <Image style={styles.image} source={{uri: property.img_url}} /> <View style={styles.heading}> <Text style={styles.price}>£{price}</Text> <Text style={styles.title}>{property.title}</Text> <View style={styles.separator}/> </View> <Text style={styles.description}>{stats}</Text> <Text style={styles.description}>{property.summary}</Text> </View> ); } } 

, API . render() .

: .

:

 module.exports = PropertyView; 

SearchResults.js require , React require :

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

rowPressed() , PropertyView :

 rowPressed(propertyGuid) { var property = this.props.listings.filter(prop => prop.guid === propertyGuid)[0]; this.props.navigator.push({ title: "Property", component: PropertyView, passProps: {property: property} }); } 

: , Cmd+R. , :



, !

, .



Xcode Info.plist NSLocationWhenInUseUsageDescription :

 PropertyFinder would like to use your location to find nearby properties – 

plist- :



, .

SearchPage.js, TouchableHighlight , 'Location', :

 onPress={this.onLocationPressed.bind(this)} 

onLocationPressed ( ).

SearchPage :

 onLocationPressed() { navigator.geolocation.getCurrentPosition( location => { var search = location.coords.latitude + ',' + location.coords.longitude; this.setState({ searchString: search }); var query = urlForQueryAndPage('centre_point', search, 1); this._executeQuery(query); }, error => { this.setState({ message: 'There was a problem with obtaining your location: ' + error }); }); } 

navigator.geolocation . Web API , , . React Native API, iOS.

. Nestoria. - , .

plist, , . , , Cmd+R. Xcode . .

, , Nestoria . Debug\Location\Custom Location … : , 55.02 -1.42 . , .

Location, .



: . , , , . . , React Native. - , , .

, , .

?

React Native. , .

-, , , JavaScript React. , , React Native, , JavaScript CSS.

, ? , Swift Objective-C? , , - .

, .

Source: https://habr.com/ru/post/J303328/


All Articles