<?xml version="1.0" encoding="utf-8" ?><rss version="2.0" xmlns:tt="http://teletype.in/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>@cccoding01</title><generator>teletype.in</generator><description><![CDATA[@cccoding01]]></description><link>https://teletype.in/@cccoding01?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=cccoding01</link><atom:link rel="self" type="application/rss+xml" href="https://teletype.in/rss/cccoding01?offset=0"></atom:link><atom:link rel="next" type="application/rss+xml" href="https://teletype.in/rss/cccoding01?offset=10"></atom:link><atom:link rel="search" type="application/opensearchdescription+xml" title="Teletype" href="https://teletype.in/opensearch.xml"></atom:link><pubDate>Sat, 04 Jul 2026 13:12:59 GMT</pubDate><lastBuildDate>Sat, 04 Jul 2026 13:12:59 GMT</lastBuildDate><item><guid isPermaLink="true">https://teletype.in/@cccoding01/_nxpb6glX-b</guid><link>https://teletype.in/@cccoding01/_nxpb6glX-b?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=cccoding01</link><comments>https://teletype.in/@cccoding01/_nxpb6glX-b?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=cccoding01#comments</comments><dc:creator>cccoding01</dc:creator><title>Создаем приложение на JavaScript с помощью React Native</title><pubDate>Tue, 18 May 2021 14:57:15 GMT</pubDate><media:content medium="image" url="https://teletype.in/files/0b/e8/0be835d2-8c6b-430d-bfcf-243ab15efeec.png"></media:content><description><![CDATA[<img src="https://teletype.in/files/f7/95/f795afbc-d938-41ed-98f1-e1e9e1809d0c.png"></img>В этом уроке мы будем изучать React Native – фреймворк от компании Facebook для создания нативных приложений под iOS и Android. У него много общего с другим очень популярным фреймворком от Facebook – React Javascript, который предназначен для построения декларативных пользовательских интерфейсов.]]></description><content:encoded><![CDATA[
  <figure class="m_original">
    <img src="https://teletype.in/files/f7/95/f795afbc-d938-41ed-98f1-e1e9e1809d0c.png" width="2535" />
  </figure>
  <p>В этом уроке мы будем изучать React Native – фреймворк от компании Facebook для создания нативных приложений под iOS и Android. У него много общего с другим очень популярным фреймворком от Facebook – <a href="https://facebook.github.io/react/" target="_blank">React Javascript</a>, который предназначен для построения декларативных пользовательских интерфейсов.</p>
  <figure class="m_original">
    <img src="https://habrastorage.org/files/7e9/19c/4d6/7e919c4d68704387ab3085d4f444aebb.jpg" width="1000" />
  </figure>
  <p>Примечание: это обновленный вариант урока, написанного Колином Эбергардом из iOS Team, содержащий ряд правок для версии React Native 0.22.</p>
  <p> Но на данный момент и так существует достаточно фреймворков, использующих JavaScript для создания iOS-приложений, таких как <a href="http://phonegap.com/" target="_blank">PhoneGap</a> или <a href="http://www.appcelerator.com/mobile-app-development-products/" target="_blank">Titanium</a>. Что же делает React Native особенным?</p>
  <p> 1. В отличие от PhoneGap, в React Native логика приложения пишется и работает на JavaScript, в то время как его интерфейс остается полностью нативным. Таким образом не требуется никаких компромиссов, характерных для HTML5 UI.<br /> 2. В отличие от Titanium, React вводит новый оригинальный и крайне эффективный подход к созданию пользовательских интерфейсов. Если говорить кратко, UI приложения выражается как функция текущего состояния приложения.</p>
  <p> Ключевая особенность React Native в том, что его разработчики намерены привнести модель программирования <a href="https://facebook.github.io/react/" target="_blank">React</a> в сферу разработки мобильных приложений. Важное уточнение: речь идет не о таком кроссплатформенном инструменте, с которым можно писать софт один раз и использовать его везде, а о таком, который можно изучить один раз и писать на нем везде. Данный урок предназначен для iOS-платформы, но, изучив весь изложенный материал, вы сможете без труда создавать также Android-приложения.</p>
  <p> Если у вас есть опыт написания приложений на Objective-C или Swift, вы наверняка не обрадуетесь идее перехода на JavaScript. Но вместе с тем, второй пункт явно должен был заинтересовать Swift-разработчиков.</p>
  <p> Несомненно, работая со Swift, вам приходилось изучать много новых и более эффективных способов шифрования алгоритмов, а также методик, способствующих преобразованию и неизменяемости. Тем не менее, способ построения UI здесь очень похож на тот, что используется при работе с Objective-C: он тоже основывается на UIKit и является императивным.</p>
  <p> React за счет таких необычных понятий как Virtual DOM и согласование переносит функциональное программирование на слой пользовательского интерфейса.</p>
  <p> В данном уроке по React Native мы будем создавать приложение по поиску недвижимости в Великобритании:</p>
  <figure class="m_original">
    <img src="https://habrastorage.org/files/360/296/865/360296865d774beaac7b879c653046c3.png" width="700" />
  </figure>
  <p>Если вы никогда прежде не работали с JavaScript, не волнуйтесь. Мы подробно разберем каждый шаг разработки. React использует для стилизации синтаксис наподобие CSS, который легко прочитать и понять, но в случае чего вы всегда можете обратиться к <a href="https://developer.mozilla.org/en-US/docs/Web/CSS" target="_blank">Mozilla Developer Network</a>.</p>
  <p> Интересно? Идем дальше.</p>
  <p> <strong>Приступаем к работе</strong></p>
  <p> Для создания JavaScript-кода React Native использует <a href="https://nodejs.org/en/" target="_blank">Node.js</a>, среду выполнения JavaScript. Если вы еще не установили себе Node.js, пора это сделать.</p>
  <p> Сначала установим <a href="http://brew.sh/" target="_blank">Homebrew</a>, следуя инструкциям на сайте, а затем – Node.js, выполнив в окне терминала следующее:</p>
  <pre>brew install node
</pre>
  <p>Затем с помощью <strong>homebrew</strong> установим <a href="https://facebook.github.io/watchman/" target="_blank">watchman</a> – сервис для отслеживания изменения и поиска файлов от Facebook:</p>
  <pre>brew install watchman
</pre>
  <p>React Native использует его, чтобы отслеживать изменения кода и делать соответствующие правки. Это что-то вроде Xcode, но выполняющего сборку каждый раз после сохранения файла.</p>
  <p> Далее установим React Native Command Line Interface (CLI), используя npm:</p>
  <pre>npm install -g react-native-cli
</pre>
  <p>Он использует <a href="https://www.npmjs.com/" target="_blank">Node Package Manager</a>, чтобы вызвать и глобально установить CLI-инструмент; npm поставляется вместе с Node.js, его функция аналогична <a href="https://cocoapods.org/" target="_blank">CocoaPods</a> или <a href="https://github.com/Carthage/Carthage" target="_blank">Carthage</a>.</p>
  <p> Для тех, кто хочет глубже разобраться с React Native, его исходный код находится <a href="https://github.com/facebook/react-native" target="_blank">в открытом доступе на GitHub</a>.</p>
  <p> Перейдите к папке, в которой вы хотите сохранить проект, и воспользуйтесь CLI-инструментом для его создания:</p>
  <pre>react-native init PropertyFinder
</pre>
  <p>Эта строка создает начальный проект, в котором содержится всё необходимое для разработки и запуска приложения на React Native.</p>
  <p> Если вы видите уведомление об устаревшей версии Node.js, убедитесь, что та, которую установил brew, является актуальной. Для этого выполните в терминале команду <strong>brew link --overwrite node</strong>.</p>
  <p> Взглянув на созданные папки и файлы, вы обнаружите папку <strong>node_modules</strong>, в которой находится фреймворк React Native. Файл <strong>index.ios.js</strong> – это макет приложения, созданный CLI-инструментом. Обратите также внимание на папку ios – в ней содержится проект Xcode и небольшой код для интеграции с Bootstrap. Наконец, там есть и компоненты для Android, но мы не будем рассматривать их здесь.</p>
  <p> Откройте файл проекта, сделайте его сборку и запустите. Симулятор отобразит следующее сообщение:</p>
  <figure class="m_original">
    <img src="https://habrastorage.org/files/dad/9bf/901/dad9bf9017c740fe8f7e4738e7825978.png" width="281" />
  </figure>
  <p><strong>Примечание</strong>: На момент написания урока начальный проект, созданный CLI-инструментом React Native, выводил три предупреждения во время сборки. Потому не волнуйтесь, впервые увидев какие-либо уведомления от Xcode. Разработчики React Native знают об этой небольшой проблеме, и мы работаем вместе с ними над ее устранением в следующем релизе React Native.</p>
  <p> Вероятно, вы также заметили всплывающее окно терминала с таким сообщением:</p>
  <pre> ┌──────────────────────────────────────────────┐
 │  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] &lt;START&gt; Building Dependency Graph
[6:15:40 PM] &lt;START&gt; Crawling File System
[6:15:40 PM] &lt;START&gt; Loading bundles layout
[6:15:40 PM] &lt;END&gt;   Loading bundles layout (0ms)
[Hot Module Replacement] Server listening on /hot
 
React packager ready.
 
[6:15:41 PM] &lt;END&gt;   Crawling File System (747ms)
[6:15:41 PM] &lt;START&gt; Building in-memory fs for JavaScript
[6:15:42 PM] &lt;END&gt;   Building in-memory fs for JavaScript (653ms)
[6:15:42 PM] &lt;START&gt; Building in-memory fs for Assets
[6:15:42 PM] &lt;END&gt;   Building in-memory fs for Assets (277ms)
[6:15:42 PM] &lt;START&gt; Building Haste Map
[6:15:42 PM] &lt;START&gt; Building (deprecated) Asset Map
[6:15:42 PM] &lt;END&gt;   Building (deprecated) Asset Map (49ms)
[6:15:42 PM] &lt;END&gt;   Building Haste Map (400ms)
[6:15:42 PM] &lt;END&gt;   Building Dependency Graph (2094ms)
</pre>
  <p>Это упаковщик React Native, работающий под управлением Node.js. Вскоре вы узнаете, для чего он нужен.</p>
  <p> Не закрывайте окно терминала, пусть оно работает на фоне. Если вы случайно закрыли его, просто остановите и перезапустите проект с помощью Xcode.</p>
  <p> <strong>Примечание</strong>: прежде чем мы заберемся в дебри кода, нужно определиться с выбором текстового редактора. Вам предстоит писать много JavaScript-кода, а Xcode явно не подходит для этого. Я использую <a href="http://www.sublimetext.com/" target="_blank">Sublime Text</a>, это недорогой и очень удобный инструмент. Но <a href="https://atom.io/" target="_blank">Atom</a>, <a href="http://brackets.io/" target="_blank">Brackets</a> или любой другой легковесный редактор тоже отлично подойдет.</p>
  <p> <strong>Hello React Native</strong></p>
  <p> Прежде чем начать работу над приложением по поиску недвижимости, мы создадим приложение Hello World!.. По ходу дела я буду вводить новые компоненты и понятия.</p>
  <p> Откройте файл <strong>index.ios.js</strong> в текстовом редакторе и удалите всё его содержимое, так как мы будем создавать приложение с нуля. Добавьте в начале файла следующее:</p>
  <pre>&#x27;use strict&#x27;;
</pre>
  <p>Эта директива объявляет <strong>строгий режим</strong>, который добавляет улучшенную обработку ошибок и налагает ограничения на некоторые элементы JavaScript. Проще говоря, он улучшает работу JavaScript.</p>
  <p> <strong>Примечание</strong>: более подробную информацию о строгом режиме можно найти в статье Джона Резига под названием <a href="http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/" target="_blank">ECMAScript 5 Strict Mode, JSON, and More</a>.</p>
  <p> Затем добавьте эту строку:</p>
  <pre>var React = require(&#x27;react-native&#x27;);
</pre>
  <p>Она загружает модуль <strong>react-native</strong> и присваивает его переменной <strong>React</strong>. React Native использует такую же технологию загрузки модуля, как и Node.js с функцией <strong>require</strong>, которая примерно эквивалентна подключению и импорту библиотек в Swift.</p>
  <p> <strong>Примечание</strong>: более подробную информацию о модулях JavaScript можно найти в <a href="https://addyosmani.com/writing-modular-js/" target="_blank">статье Эдди Османи о модульном JavaScript</a>.</p>
  <p> Далее добавьте следующее:</p>
  <pre>var styles = React.StyleSheet.create({
  text: {
    color: &#x27;black&#x27;,
    backgroundColor: &#x27;white&#x27;,
    fontSize: 30,
    margin: 80
  }
});
</pre>
  <p>Этот код задает единый стиль, который мы вскоре применим к тексту Hello World!.. Если у вас уже есть какой-либо опыт веб-разработки, вероятно, вы узнали эти свойства. Внешний вид класса StyleSheet, используемого для стилизации интерфейса, напоминает синтаксис широко применяемого в вебе языка <a href="https://developer.mozilla.org/en-US/docs/Web/CSS" target="_blank">Cascading Style Sheets (CSS)</a>.</p>
  <p> Итак, займемся непосредственно приложением. Добавьте следующий код прямо под переменной со стилями:</p>
  <pre>class PropertyFinderApp extends React.Component {
  render() {
    return React.createElement(React.Text, {style: styles.text}, &quot;Hello World!&quot;);
  }
}
</pre>
  <p>Да, это класс JavaScript.</p>
  <p> Классы были добавлены в ECMAScript 6 (ES6). Поскольку JavaScript постоянно развивается, разработчики вынуждены ограничивать себя в используемых средствах ради сохранения совместимости со старыми системами или браузерами. И хотя iOS 9 не полностью поддерживает ES6, React Native использует инструмент под названием <a href="https://babeljs.io/" target="_blank">Babel</a>, который автоматически переводит современный JavaScript в совместимый с устаревшими версиями JavaScript там, где это необходимо.</p>
  <p> <strong>Примечание</strong>: если вы веб-разработчик, вы также можете использовать Babel в браузере. Так что теперь действительно не осталось оправданий для работы со старыми версиями JavaScript – даже для поддержки устаревших версий браузеров.</p>
  <p> <strong>PropertyFinderApp</strong> расширяет <strong>React.Component</strong>, основной структурный элемент интерфейса React. Компоненты содержат неизменяемые свойства и изменяемые переменные состояния; они предоставляют метод для рендеринга. Приложение, над которым мы сейчас работаем, очень простое и нуждается только в методе рендеринга.</p>
  <p> Компоненты React Native – это не классы UIKit, а их легковесные эквиваленты. Фреймворк обеспечивает преобразование дерева компонентов React в требуемый нативный интерфейс.</p>
  <p> Наконец, добавим в конец файла эту строку:</p>
  <pre>React.AppRegistry.registerComponent(&#x27;PropertyFinder&#x27;, function() { return PropertyFinderApp });
</pre>
  <p><strong>AppRegistry</strong> определяет точку входа в приложение и предоставляет корневой компонент.</p>
  <p> Сохраните изменения в <strong>index.ios.js</strong> и вернитесь к Xcode. Убедитесь, что схема <strong>PropertyFinder</strong> выбрана с одним из симуляторов iPhone, а затем соберите и запустите ваш проект. Через несколько секунд на экране отобразится ваше приложение Hello World!:</p>
  <figure class="m_original">
    <img src="https://habrastorage.org/files/5f4/1b1/f60/5f41b1f608b44e089a214d053bff7e73.png" width="281" />
  </figure>
  <p>Это JavaScript-приложение, работающее на симуляторе, который отображает нативный UI – и это без помощи браузера.</p>
  <p> Всё еще не верите? Убедитесь сами: выберите в Xcode <strong>Debug\View Debugging\Capture View Hierarchy</strong> и вы увидите нативную иерархию представлений. Вы также заметите повсюду сущности <strong>UIWebView</strong>. Тест приложения отображается в <strong>RCTText</strong>. Но что это такое? Вернитесь в Xcode, выберите <strong>File\Open Quickly…</strong> и введите <strong>RCTView.h.</strong> Обратите внимание, что RCTView наследует непосредственно от <strong>UIView.</strong> Выходит, всё работает отлично.</p>
  <figure class="m_original">
    <img src="https://habrastorage.org/files/e82/48a/8dc/e8248a8dc1bd458293c5c6f065624867.png" width="1648" />
  </figure>
  <p>Хотите знать, как это работает? Откройте в Xcode <strong>AppDelegate.m</strong> и определите расположение <strong>application:didFinishLaunchingWithOptions:</strong>. Этот метод создает <strong>RCTRootView</strong>, который загружает JavaScript-приложение и рендерит результирующее представление.</p>
  <p> Когда приложение запускается, RCTRootView загружает приложение из этого URL:</p>
  <pre>http://localhost:8081/index.ios.bundle
</pre>
  <p>Вспомните окно терминала, которое было открыто, когда вы запускали это приложение. Оно запускает упаковщик и сервер, который обрабатывает запрос выше.</p>
  <p> Откройте этот URL в Safari, и вы увидите JavaScript-код вашего приложения. Вы также должны обнаружить там код Hello World!, встроенный во фреймворк React Native.</p>
  <p> Когда ваше приложение запускается, этот код загружается и выполняется фреймворком JavaScriptCore. В нашем случае он загружает компонент <strong>PropertyFinderApp</strong> и затем выстраивает нативное UIKit представление. Дальше в уроке мы поговорим об этом подробнее.</p>
  <p> <strong>Hello World JSX</strong></p>
  <p> Созданное приложение использует <strong>React.createElement</strong> для построения простого интерфейса, преобразовываемого в нативный эквивалент с помощью React. И хотя текущий JavaScript-код читается легко, в случае более сложного UI со вложенными элементами он может превратиться в кашу.</p>
  <p> Убедитесь, что приложение еще работает, затем вернитесь к редактированию файла <strong>index.ios.js</strong> и измените оператор <strong>return</strong> следующим образом:</p>
  <pre>return &lt;React.Text style={styles.text}&gt;Hello World (Again)&lt;/React.Text&gt;;
</pre>
  <p>Это <a href="https://facebook.github.io/react/docs/jsx-in-depth.html" target="_blank">JSX</a>, расширение синтаксиса JavaScript, которое добавляет в JavaScript-код синтаксис наподобие HTML. Те, у кого уже есть опыт веб-разработки, заметят сходство с последним. Мы будем использовать JSX на протяжении всего урока.</p>
  <p> Сохраните изменения в <strong>index.ios.js</strong> и вернитесь в симулятор. Нажмите Cmd+R, чтобы обновить сообщение на экране:</p>
  <figure class="m_original">
    <img src="https://habrastorage.org/files/590/f19/9aa/590f199aa2a54e309cee507547ba94af.png" width="281" />
  </figure>
  <p>Перезапустить приложение на React Native так же просто, как обновить страницу браузера. Обратите внимание, что в таком случае отобразятся только те изменения, которые касались JavaScript-файлов. Во всех других случаях потребуется повторная сборка приложения в Xcode.</p>
  <p> Поскольку в этом уроке мы будем работать с тем же набором JavaScript-компонентов, вы можете оставить приложение работать и обновлять его после сохранения изменений в <strong>index.ios.js</strong>.</p>
  <p> <strong>Примечание</strong>: если вам интересно, во что преобразовывается JSX, взгляните на ‘bundle’ в браузере.</p>
  <p> Полагаю, мы вполне наигрались с Hello World!, теперь пришло время создать настоящее приложение. </p>
  <p> <strong>Добавляем навигацию</strong></p>
  <p> Приложение Property Finder использует стандартную стековую навигацию, предоставленную навигационным контроллером UIKit. Добавим это поведение.</p>
  <p> В файле <strong>index.ios.js</strong> переименуйте класс <strong>PropertyFinderApp</strong> в <strong>HelloWorld:</strong></p>
  <pre>class HelloWorld extends React.Component {
</pre>
  <p>Оставим пока текст Hello World!, но он больше не будет корневым компонентом приложения.</p>
  <p> Затем добавим ниже компонента <strong>HelloWorld</strong> следующий класс:</p>
  <pre>class PropertyFinderApp extends React.Component {
  render() {
    return (
      &lt;React.NavigatorIOS
        style={styles.container}
        initialRoute={{
          title: &#x27;Property Finder&#x27;,
          component: HelloWorld,
        }}/&gt;
    );
  }
}
</pre>
  <p>Он создает навигационный контроллер, применяет стиль и устанавливает первоначальный маршрут к компоненту HelloWorld. В веб-разработке <strong>маршрутизация</strong> – это способ определения навигационной структуры приложения, где страницы – или <em>маршруты</em> – привязываются к соответствующим URL.</p>
  <p> Далее подкорректируйте стили, добавив туда параметры контейнера, как показано ниже:</p>
  <pre>var styles = React.StyleSheet.create({
  text: {
    color: &#x27;black&#x27;,
    backgroundColor: &#x27;white&#x27;,
    fontSize: 30,
    margin: 80
  },
  container: {
    flex: 1
  }
});
</pre>
  <p>О том, что такое <strong>flex: 1</strong>, вы узнаете немного позже.</p>
  <p> Сохраните изменения, вернитесь в симулятор и нажмите Cmd+R, чтобы увидеть обновленный интерфейс:</p>
  <figure class="m_original">
    <img src="https://habrastorage.org/files/1b4/8b2/33e/1b48b233ebc8474087c5307c773afde6.png" width="281" />
  </figure>
  <p>Корневое представление навигационного контроллера соответствует тексту Hello World!.. Теперь у нас есть базовая навигационная структура текущего приложения. Пора добавить настоящий UI.</p>
  <p> <strong>Создаем страницу поиска</strong></p>
  <p> Добавьте в проект новый файл под названием <strong>SearchPage.js</strong> и поместите его в одну папку с файлом <strong>index.ios.js</strong>. Добавьте в новый файл этот код:</p>
  <pre>&#x27;use strict&#x27;;
 
var React = require(&#x27;react-native&#x27;);
var {
  StyleSheet,
  Text,
  TextInput,
  View,
  TouchableHighlight,
  ActivityIndicatorIOS,
  Image,
  Component
} = React;
</pre>
  <p>Мы уже рассматривали строгий режим и импорт в react-native, но следующий оператор присвоения – это нечто другое.</p>
  <p> Это деструктурирующее присваивание, позволяющее извлекать множество свойств объекта и присваивать их переменным с одним оператором. Как результат, в оставшемся коде можно отбросить префикс <strong>React</strong>. К примеру, можно обращаться напрямую к <strong>StyleSheet</strong>, а не к <strong>React.StyleSheet</strong>. Деструктурирование также очень удобно использовать для управления массивами. Более подробную информацию о нем <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment" target="_blank">можно найти в этой статье</a>.</p>
  <p> Не закрывая файл <strong>SearchPage.js</strong>, добавьте внизу этот стиль:</p>
  <pre>var styles = StyleSheet.create({
  description: {
    marginBottom: 20,
    fontSize: 18,
    textAlign: &#x27;center&#x27;,
    color: &#x27;#656565&#x27;
  },
  container: {
    padding: 30,
    marginTop: 65,
    alignItems: &#x27;center&#x27;
  }
});
</pre>
  <p>Это тоже стандартные CSS-свойства. Возможно, данный способ задания стилей покажется вам менее удобным, чем использование Interface Builder, но этот подход определенно лучше, чем задавать свойства представления по одному в методах viewDidLoad().</p>
  <p> Вставьте сам компонент непосредственно под стилями:</p>
  <pre>class SearchPage extends Component {
  render() {
    return (
      &lt;View style={styles.container}&gt;
        &lt;Text style={styles.description}&gt;
          Search for houses to buy!
        &lt;/Text&gt;
        &lt;Text style={styles.description}&gt;
          Search by place-name, postcode or search near your location.
        &lt;/Text&gt;
      &lt;/View&gt;
    );
  }
}
</pre>
  <p><strong>render</strong> отлично демонстрирует JSX и его структуру. Наряду со стилем вы можете очень просто визуализировать интерфейс, созданный этим компонентом: контейнер с двумя текстовыми подписями.</p>
  <p> Напоследок добавим следующую строку в конец файла:</p>
  <pre>module.exports = SearchPage;
</pre>
  <p>Она экспортирует класс <strong>SearchPage</strong>, что позволяет использовать его в других файлах.</p>
  <p> Следующий шаг – обновить маршрутизацию приложения, чтобы установить другой первоначальный маршрут.</p>
  <p> Откройте <strong>index.ios.js</strong> и добавьте эту строку сразу после <strong>require</strong> в начале файла:</p>
  <pre>var SearchPage = require(&#x27;./SearchPage&#x27;);
</pre>
  <p>В функции <strong>render</strong> класса <strong>PropertyFinderApp</strong> обновите <strong>initialRoute</strong>, чтобы привязать только что созданную страницу, как показано ниже:</p>
  <pre>component: SearchPage
</pre>
  <p>Теперь, если хотите, можно удалить класс <strong>HelloWorld</strong> и его стили. Они вам больше не понадобятся. <br /> Сохраните изменения, вернитесь в симулятор и нажмите Cmd+R, чтобы увидеть обновленный интерфейс:</p>
  <figure class="m_original">
    <img src="https://habrastorage.org/files/5ce/2ef/d35/5ce2efd35ce943e6b03a26be8cbbc985.png" width="281" />
  </figure>
  <p>Здесь используется новый компонент <strong>SearchPage</strong>.</p>
  <p> <strong>Стилизуем с помощью Flexbox</strong></p>
  <p> В этом уроке мы уже имели дело с некоторыми базовыми свойствами CSS, задающими параметры цвета, а также внутренних и внешних отступов. Тем не менее, вероятно, вы еще не слышали о <strong>flexbox</strong>. Эта технология лишь недавно была добавлена в спецификацию CSS, она очень полезна при построении макетов пользовательских интерфейсов.</p>
  <p> React Native использует библиотеку <a href="https://github.com/facebook/css-layout" target="_blank">css-layout</a>, которая является JavaScript-реализацией flexbox-стандарта, скомпилированного на C (для iOS) и на Java (для Android).<br /> Очень хорошо, что React Native создавался как отдельный проект, нацеленный на несколько языков программирования, так как это позволяет разрабатывать приложения с использованием новейших подходов, вроде применения <a href="http://blog.scottlogic.com/2015/02/02/svg-layout-flexbox.html" target="_blank">flexbox-макетов к SVG</a>.</p>
  <p> По умолчанию контейнер в вашем приложении имеет направление потока данных в виде столбца, что соответствует параметру column – а значит, всё содержимое контейнера будет выстраиваться вертикально:</p>
  <figure class="m_original">
    <img src="https://habrastorage.org/files/50d/7fb/53d/50d7fb53d8e94c18b4a95ff7874e528b.png" width="634" />
  </figure>
  <p>Это так называемая главная ось, или <strong>main axis</strong>, она может иметь как горизонтальное, так и вертикальное направление.</p>
  <p> Вертикальное положение каждого дочернего элемента контейнера высчитывается с учетом его внешних и внутренних отступов, а также высоты. Контейнер также задает свойству <strong>alignItems</strong> значение <strong>center</strong>, что определяет положение дочерних элементов на главной оси. В данном случае мы получим текст с выравниванием по центру.</p>
  <p> Теперь добавим поле ввода и кнопки. Откройте файл <strong>SearchPage.js</strong> и введите следующий код сразу после закрывающего тега второго элемента <strong>Text</strong>:</p>
  <pre>&lt;View style={styles.flowRight}&gt;
  &lt;TextInput
    style={styles.searchInput}
    placeholder=&#x27;Search via name or postcode&#x27;/&gt;
  &lt;TouchableHighlight style={styles.button}
      underlayColor=&#x27;#99d9f4&#x27;&gt;
    &lt;Text style={styles.buttonText}&gt;Go&lt;/Text&gt;
  &lt;/TouchableHighlight&gt;
&lt;/View&gt;
&lt;TouchableHighlight style={styles.button}
    underlayColor=&#x27;#99d9f4&#x27;&gt;
  &lt;Text style={styles.buttonText}&gt;Location&lt;/Text&gt;
&lt;/TouchableHighlight&gt;
</pre>
  <p>Мы добавили два представления высшего уровня: в одном из них находится текстовое поле ввода и кнопка, а в другом – еще одна кнопка. Сейчас вы узнаете, как можно стилизовать эти элементы</p>
  <p> Вернитесь к параметрам стилей, поставьте запятую после блока <strong>container</strong> и добавьте ниже новые стили:</p>
  <pre>flowRight: {
  flexDirection: &#x27;row&#x27;,
  alignItems: &#x27;center&#x27;,
  alignSelf: &#x27;stretch&#x27;
},
buttonText: {
  fontSize: 18,
  color: &#x27;white&#x27;,
  alignSelf: &#x27;center&#x27;
},
button: {
  height: 36,
  flex: 1,
  flexDirection: &#x27;row&#x27;,
  backgroundColor: &#x27;#48BBEC&#x27;,
  borderColor: &#x27;#48BBEC&#x27;,
  borderWidth: 1,
  borderRadius: 8,
  marginBottom: 10,
  alignSelf: &#x27;stretch&#x27;,
  justifyContent: &#x27;center&#x27;
},
searchInput: {
  height: 36,
  padding: 4,
  marginRight: 5,
  flex: 4,
  fontSize: 18,
  borderWidth: 1,
  borderColor: &#x27;#48BBEC&#x27;,
  borderRadius: 8,
  color: &#x27;#48BBEC&#x27;
}
</pre>
  <p>Следите за форматированием: каждое свойство стиля или селектор следует отделять запятой.</p>
  <p> Эти стили предназначены для только что добавленных поля ввода и кнопок.</p>
  <p> Сохраните изменения, вернитесь в симулятор и нажмите Cmd+R, чтобы увидеть обновленный интерфейс:</p>
  <figure class="m_original">
    <img src="https://habrastorage.org/files/e87/b13/501/e87b135015ed4b62907646c28be1a610.png" width="281" />
  </figure>
  <p>Текстовое поле и кнопка ‘Go’ находятся на одной строке, так как вы поместили их в контейнер со стилем <strong>flowRight</strong>, элементы которого выстраиваются в строку за счет свойства <strong>flexDirection: &#x27;row&#x27;</strong>. Вместо того чтобы жестко задавать ширину каждого из этих элементов, мы выставили им относительную ширину с помощью значений свойства <strong>flex</strong>. Таким образом, в селекторе текстового поля searchInput мы имеем <strong>flex: 4</strong>, а в селекторе кнопки <strong>button — flex: 1</strong>, вследствие чего их соотношение составляет 4:1.</p>
  <p> Возможно, вы также заметили, что элементы, которые мы называем кнопками, по сути таковыми не являются. На самом деле, кнопки в UIKit – это всего лишь интерактивные текстовые надписи. Кнопки вашего приложения используют компонент React Native под названием <strong>TouchableHighlight</strong>, который по нажатию становится прозрачным и показывает нижележащий цвет.</p>
  <p> Наконец, добавим на страницу поиска изображение. Вы можете скачать его в нескольких разрешениях <a href="http://www.raywenderlich.com/wp-content/uploads/2015/03/ReactNative-HouseImage.zip" target="_blank">одним архивом</a>. После скачивания распакуйте архив.</p>
  <p> Далее создадим в корневом проекте директорию под названием ’Resources’ и поместим в нее все три изображения.</p>
  <p> <strong>Предметные каталоги</strong>: Как вам известно, специалисты из Apple рекомендуют по возможности помещать изображения в предметные каталоги. Тем не менее, для React Native это <a href="http://facebook.github.io/react-native/docs/images.html#content" target="_blank">наоборот нежелательно</a>. Хранение цифровых объектов приложения рядом с его компонентами дает несколько преимуществ. Во-первых, это позволяет сохранить независимость компонентов. Во-вторых, при добавлении новых изображений не требуется повторная загрузка приложения. И в-третьих, при разработке приложения для iOS и Android это дает возможность хранить изображения для двух платформ в одном месте.</p>
  <p> Вернитесь к файлу <strong>SearchPage.js</strong> и добавьте эту строку под закрывающим тегом компонента <strong>TouchableHighlight</strong>, отвечающего за кнопку <strong>location</strong>:</p>
  <pre>&lt;Image source={require(&#x27;./Resources/house.png&#x27;)} style={styles.image}/&gt;
</pre>
  <p>Теперь добавьте соответствующий стиль изображения в блоке со стилями, не забыв поставить запятую после предыдущего селектора:</p>
  <pre>image: {
  width: 217,
  height: 138
}
</pre>
  <p>Сохраните изменения. Вернитесь в симулятор и нажмите Cmd+R, чтобы увидеть новый интерфейс:</p>
  <figure class="m_original">
    <img src="https://habrastorage.org/files/60d/14f/b1e/60d14fb1e92b4e079148b3fc51dacf40.png" width="281" />
  </figure>
  <p><strong>Примечание</strong>: Если изображение с домом не отображается, а вместо него показано уведомление о том, что картинка не найдена, попробуйте перезапустить упаковщик с помощью команды npm start в терминале.</p>
  <p> Наше приложение уже смотрится симпатично, но всё же чего-то не хватает. Нужно добавить состояние приложения и выполнить кое-какие действия.</p>
  <p> <strong>Добавляем состояние компонента</strong></p>
  <p> Каждый компонент в React имеет собственный объект состояния, который используется как хранилище типа «ключ–значение». Прежде чем компонент отобразится, нужно задать начальное состояние.</p>
  <p> В файле <strong>SearchPage.js</strong> добавьте следующий код в класс <strong>SearchPage</strong>, прямо перед <strong>render()</strong>:</p>
  <pre>constructor(props) {
  super(props);
  this.state = {
    searchString: &#x27;london&#x27;
  };
}
</pre>
  <p>Теперь у вашего компонента есть переменная <strong>state</strong>, а начальным значением <strong>searchString</strong> является <strong>london</strong>.</p>
  <p> Время воспользоваться этим состоянием компонента. Изменим элемент <strong>TextInput</strong> в <strong>render</strong>, как показано ниже:</p>
  <pre>&lt;TextInput
  style={styles.searchInput}
  value={this.state.searchString}
  placeholder=&#x27;Search via name or postcode&#x27;/&gt;
</pre>
  <p>Мы выставили значение свойства <strong>TextInput</strong> – то есть показываемого пользователю текста – на текущее значение переменной состояния <strong>searchString</strong>. Итак, мы позаботились о начальном состоянии. Но что будет, когда пользователь отредактирует этот текст?</p>
  <p> Прежде всего, создадим метод, выступающий в роли обработчика событий. Перейдите к классу <strong>SearchPage</strong> и добавьте данный метод сразу после <strong>constructor</strong>:</p>
  <pre>onSearchTextChanged(event) {
  console.log(&#x27;onSearchTextChanged&#x27;);
  this.setState({ searchString: event.nativeEvent.text });
  console.log(this.state.searchString);
}
</pre>
  <p>Он берет значение из свойства <strong>text</strong> в событии родного браузера и использует его для обновления состояния компонента. Он также добавляет код для сбора данных, которые нам вскоре пригодятся.</p>
  <p> Чтобы данный метод вызывался каждый раз при изменении текста, вернемся к полю <strong>TextInput</strong> метода <strong>render</strong> и добавим свойство <strong>onChange</strong>. В итоге тег примет следующий вид:</p>
  <pre>&lt;TextInput
  style={styles.searchInput}
  value={this.state.searchString}
  onChange={this.onSearchTextChanged.bind(this)}
  placeholder=&#x27;Search via name or postcode&#x27;/&gt;
</pre>
  <p>Когда пользователь меняет текст, вызывается функция, добавленная к свойству <strong>onChange</strong> (в данном случае это <strong>onSearchTextChanged</strong>).</p>
  <p> <strong>Примечание</strong>: Возможно, вам непонятно, для чего нужно выражение <strong>bind(this)</strong>. JavaScript интерпретирует ключевое слово <strong>this</strong> немного иначе, чем большинство других языков. В Swift данному слову соответствует <strong>self</strong>. Использование <strong>bind</strong> в данном контексте гарантирует, что <strong>this</strong> внутри метода <strong>onSearchTextChanged</strong> является отсылкой к экземпляру компонента. Дополнительную информацию о ключевом слове <strong>this</strong> можно получить на <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this" target="_blank">MDN</a>.</p>
  <p> Прежде чем мы снова обновим приложение, добавим оператор log в начале <strong>render()</strong>, сразу перед <strong>return</strong>:</p>
  <pre>console.log(&#x27;SearchPage.render&#x27;);
</pre>
  <p>Вам предстоит узнать нечто очень любопытное об этих операторах.</p>
  <p> Сохраните изменения, вернитесь в симулятор и нажмите Cmd+R. Вы увидите, что теперь начальным значением поля ввода является ‘london’, а при редактировании текста в консоль Xcode записываются какие-то выражения:</p>
  <figure class="m_original">
    <img src="https://habrastorage.org/files/616/2d2/6a0/6162d26a03d64727a6d331f77aeed419.png" width="235" />
  </figure>
  <p>Если вы внимательно посмотрите на скриншот выше, то можете заподозрить, что порядок записи выражений немного нарушен:</p>
  <p> 1. Это первоначальный вызов <strong>render()</strong>, необходимый чтобы настроить представление.<br /> 2. При изменении текста вызывается <strong>onSearchTextChanged()</strong>.<br /> 3. Затем обновляется состояние компонента, чтобы отобразить только что введенный текст, который повторно запускает <strong>render</strong>. <br /> 4. <strong>onSearchTextChanged()</strong> завершает всё, записывая новую строку поиска.</p>
  <p> Когда приложение обновляет состояние любого компонента React, это запускает повторный рендеринг всего пользовательского интерфейса, который, в свою очередь, вызывает <strong>render</strong> всех компонентов. Это превосходная идея, так как в этом случае логика рендеринга полностью отделяется от изменений состояния, которые затрагивают UI. </p>
  <p> В большинстве UI-фреймворков разработчику нужно либо вручную обновлять интерфейс в зависимости от изменений состояния, либо делать это посредством каких-либо вспомогательных фреймворков, которые создают неявную связь между состоянием приложения и его представлением в UI. Что касается второго варианта, по этому поводу можно ознакомиться со статьей о применении <a href="https://www.raywenderlich.com/74106/mvvm-tutorial-with-reactivecocoa-part-1" target="_blank">шаблона MVVM совместно с фреймворком ReactiveCocoa</a>.</p>
  <p> С React вам больше не придется волноваться о том, какие части UI могут быть затронуты изменением состояния, так как весь интерфейс выражается в виде функции состояния приложения.</p>
  <p> На данном этапе вы, скорее всего, заметили один изъян данного подхода. Совершенно верно, дело в производительности.</p>
  <p> Конечно, нельзя просто так отбрасывать весь интерфейс и перестраивать его каждый раз, когда что-то меняется. Вот где React по-настоящему проявляет себя. Каждый раз, когда интерфейс рендерится, он берет дерево видимых объектов, которое возвращают методы render, и согласовывает его с текущим представлением UIKit. В результате этого согласования получается список обновлений, которые React должен применить к текущему представлению. Таким образом, повторному рендерингу подвергнутся только те объекты, которые на самом деле были изменены.</p>
  <p> Разве не здорово видеть, как в нашем iOS-приложении применяются новейшие подходы, за счет которых ReactJS является таким уникальным: virtual-DOM (объектная модель документа, визуальное дерево веб-документа) и согласование?</p>
  <p> Позже у вас будет возможность разобраться с этим подробнее, а пока нужно еще поработать над приложением. Для начала удалим код сбора данных, который мы добавили ранее, так как он только засоряет общий код.</p>
  <p> <strong>Настраиваем поиск</strong></p>
  <p> Чтобы реализовать поиск, нужно обработать нажатие кнопки ‘Go’, создать необходимый API-запрос и предоставить пользователю визуальное подтверждение того, что запрос обрабатывается.</p>
  <p> Откройте файл <strong>SearchPage.js</strong>, найдите constructor и обновите внутри него начальное состояние:</p>
  <pre>this.state = {
  searchString: &#x27;london&#x27;,
  isLoading: false
};
</pre>
  <p>Новое свойство <strong>isLoading</strong> будет следить за тем, обрабатывается ли запрос. </p>
  <p> Добавьте такую логику в начале <strong>render</strong>:</p>
  <pre>var spinner = this.state.isLoading ?
  ( &lt;ActivityIndicatorIOS
      size=&#x27;large&#x27;/&gt; ) :
  ( &lt;View/&gt;);
</pre>
  <p>Это тернарный оператор <strong>if</strong>, который либо добавляет индикатор выполнения действия, либо отображает пустой экран – в зависимости от состояния компонента <strong>isLoading</strong>. Поскольку весь компонент рендерится каждый раз заново, вы можете спокойно смешивать логику JSX и JavaScript.</p>
  <p> Чтобы добавить на страницу индикатор загрузки, перейдите к JSX, отвечающему за интерфейс поиска в <strong>return</strong>, и вставьте под <strong>Image</strong> эту строку:</p>
  <pre>{spinner}
</pre>
  <p>Затем добавьте данные методы в класс <strong>SearchPage</strong>:</p>
  <pre>_executeQuery(query) {
  console.log(query);
  this.setState({ isLoading: true });
}
 
onSearchPressed() {
  var query = urlForQueryAndPage(&#x27;place_name&#x27;, this.state.searchString, 1);
  this._executeQuery(query);
}
</pre>
  <p>Метод <strong>_executeQuery()</strong> впоследствии будет выполнять запрос, но пока он просто заносит сообщение в консоль и необходимым образом настраивает компонент <strong>isLoading</strong>, чтобы в интерфейсе отобразилось новое состояние.</p>
  <p> <strong>Примечание</strong>: Классы в JavaScript не имеют модификаторов доступа, потому ключевого слова ‘private’ для них тоже не существует. В силу этого многие разработчики часто дают методам префикс в виде нижнего подчеркивания, чтобы указать, что они являются private.</p>
  <p> Метод <strong>onSearchPressed()</strong> настраивает и отправляет запрос. Он должен срабатывать по нажатию кнопки ‘Go’. Чтобы реализовать это, вернитесь к <strong>render</strong> и добавьте следующее свойство внутри открывающего тега компонента <strong>TouchableHighlight</strong>, отвечающего за текст ‘Go’:</p>
  <pre>onPress={this.onSearchPressed.bind(this)}
</pre>
  <p>Наконец, добавьте эту служебную функцию над объявлением класса SearchPage:</p>
  <pre>function urlForQueryAndPage(key, value, pageNumber) {
  var data = {
      country: &#x27;uk&#x27;,
      pretty: &#x27;1&#x27;,
      encoding: &#x27;json&#x27;,
      listing_type: &#x27;buy&#x27;,
      action: &#x27;search_listings&#x27;,
      page: pageNumber
  };
  data[key] = value;
 
  var querystring = Object.keys(data)
    .map(key =&gt; key + &#x27;=&#x27; + encodeURIComponent(data[key]))
    .join(&#x27;&amp;&#x27;);
 
  return &#x27;http://api.nestoria.co.uk/api?&#x27; + querystring;
};
</pre>
  <p>Эта функция не зависит от <strong>SearchPage</strong>, потому она реализуется скорее как свободная функция, а не как метод. Сначала она создает строку запроса, основываясь на параметрах данных. Исходя из них, она преобразовывает данные в требуемый формат строки: пары name=value, разделенные амперсандами. Синтаксис =&gt; соответствует стрелочной функции, еще одному <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions" target="_blank">недавнему дополнению в JavaScript</a>. Стрелочные функции предоставляют более лаконичный синтаксис для создания анонимных функций.</p>
  <p> Сохраните изменения, вернитесь к симулятору, перезагрузите страницу с помощью комбинации Cmd+R и нажмите кнопку ‘Go’. На экране отобразится индикатор загрузки. Обратите внимание на консоль Xcode:</p>
  <figure class="m_original">
    <img src="https://habrastorage.org/files/1f7/b64/026/1f7b640260fe4b958e620b6588125a08.png" width="700" />
  </figure>
  <p>На экране показан индикатор активности, а в логе появляется URL для требуемого запроса. Откройте этот URL в браузере, чтобы увидеть результат: большой JSON-объект. Но не волнуйтесь, вам не нужно будет вникать в него. Сейчас мы добавим код и проведем его парсинг. </p>
  <p> <strong>Примечание</strong>: Данное приложение использует для <a href="http://www.nestoria.co.uk/help/api" target="_blank">поиска недвижимости Nestoria API</a>. JSON-ответ, который приходит из API, весьма незамысловат. Так или иначе, вы всегда можете просмотреть документацию на предмет предполагаемого формата URL-запросов и ответов. </p>
  <p> Следующий шаг – выполнить запрос из приложения.</p>
  <p> <strong>Выполняем API-запрос</strong></p>
  <p> Не закрывая файл <strong>SearchPage.js</strong>, обновите начальное положение в конструкторе класса, чтобы добавить переменную <strong>message</strong>:</p>
  <pre>this.state = {
  searchString: &#x27;london&#x27;,
  isLoading: false,
  message: &#x27;&#x27;
};
</pre>
  <p>Найдите <strong>render</strong> и добавьте внутри него следующую строку:</p>
  <pre>&lt;Text style={styles.description}&gt;{this.state.message}&lt;/Text&gt;
</pre>
  <p>Она отвечает за отображение внизу экрана сообщений для пользователя. </p>
  <p> Найдите класс <strong>SearchPage</strong> и добавьте в конец метода <strong>_executeQuery()</strong> данный код:</p>
  <pre>fetch(query)
  .then(response =&gt; response.json())
  .then(json =&gt; this._handleResponse(json.response))
  .catch(error =&gt;
     this.setState({
      isLoading: false,
      message: &#x27;Something bad happened &#x27; + error
   }));
</pre>
  <p>Он использует функцию <strong>fetch</strong>, которая является <a href="https://fetch.spec.whatwg.org/" target="_blank">частью Web API</a> и предоставляет значительно улучшенный API по сравнению с XMLHttpRequest. Асинхронный ответ возвращается в виде объекта <a href="http://www.html5rocks.com/en/tutorials/es6/promises/" target="_blank">promise</a>, и в случае успеха выполняется парсинг JSON-объекта, который затем передается в метод <strong>_handleResponse</strong> (мы добавим его немного позже). </p>
  <p> Наконец, добавим эту функцию в <strong>SearchPage</strong>:</p>
  <pre>_handleResponse(response) {
  this.setState({ isLoading: false , message: &#x27;&#x27; });
  if (response.application_response_code.substr(0, 1) === &#x27;1&#x27;) {
    console.log(&#x27;Properties found: &#x27; + response.listings.length);
  } else {
    this.setState({ message: &#x27;Location not recognized; please try again.&#x27;});
  }
}
</pre>
  <p>Она очищает <strong>isLoading</strong> и при успешном запросе добавляет в лог количество найденных объектов недвижимости.</p>
  <p> <strong>Примечание</strong>: В Nestoria API есть ряд весьма полезных <a href="http://www.nestoria.co.uk/help/api-return-codes" target="_blank">кодов ответа сервера</a>. К примеру, коды 202 и 200 возвращают список локаций, подобранных по принципу наилучшего выбора. Почему бы не воспользоваться этой опцией и не предоставить пользователям несколько дополнительных предложений?</p>
  <p> Сохраните изменения, вернитесь в симулятор и нажмите Cmd+R. Попробуйте ввести поисковый запрос ‘london’. Вы должны увидеть в логе сообщение о том, что было найдено 20 объектов недвижимости (стандартное для результата количество). А теперь попробуйте ввести неверный запрос, например ‘narnia’. Вы увидите следующее сообщение:</p>
  <figure class="m_original">
    <img src="https://habrastorage.org/files/506/bb9/eb8/506bb9eb84c64a9f880a824d7ee3f6bb.png" width="281" />
  </figure>
  <p>Время поработать над выводом результата поиска на экран.</p>
  <p> <strong>Выводим результат поиска</strong></p>
  <p> Создайте новый файл под названием <strong>SearchResults.js</strong> и добавьте в него следующее:</p>
  <pre>&#x27;use strict&#x27;;
 
var React = require(&#x27;react-native&#x27;);
var {
  StyleSheet,
  Image,
  View,
  TouchableHighlight,
  ListView,
  Text,
  Component
} = React;
</pre>
  <p>Всё верно, это выражение <strong>require</strong>, включающее модуль <strong>react-native</strong> и деструктурирующее присваивание.</p>
  <p> Затем добавьте сам компонент:</p>
  <pre>class SearchResults extends Component {
 
  constructor(props) {
    super(props);
    var dataSource = new ListView.DataSource(
      {rowHasChanged: (r1, r2) =&gt; r1.guid !== r2.guid});
    this.state = {
      dataSource: dataSource.cloneWithRows(this.props.listings)
    };
  }
 
  renderRow(rowData, sectionID, rowID) {
    return (
      &lt;TouchableHighlight
          underlayColor=&#x27;#dddddd&#x27;&gt;
        &lt;View&gt;
          &lt;Text&gt;{rowData.title}&lt;/Text&gt;
        &lt;/View&gt;
      &lt;/TouchableHighlight&gt;
    );
  }
 
  render() {
    return (
      &lt;ListView
        dataSource={this.state.dataSource}
        renderRow={this.renderRow.bind(this)}/&gt;
    );
  }
 
}
</pre>
  <p>Код выше использует специальный компонент – <strong>ListView</strong>, который отображает данные внутри контейнера прокрутки в несколько рядов, почти как в <strong>UITableView</strong>. Данные попадают в <strong>ListView</strong> через <strong>ListView.DataSource</strong> и функцию, передающую UI для каждого ряда.</p>
  <p> Создавая источник данных, вы предоставляете функцию, которая проверяет на идентичность пару рядов. <strong>ListView</strong> затем использует результат во время процесса согласования, чтобы выявить изменения в данных списка. В нашем примере Nestoria API возвращает объекты недвижимости со свойством <strong>guid</strong>, что хорошо подходит для наших целей.</p>
  <p> Добавьте в конец файла экспорт модуля:</p>
  <pre>module.exports = SearchResults;
</pre>
  <p>А эту строку нужно вставить в начале файла <strong>SearchPage.js</strong>, ниже вызова <strong>require</strong> для React:</p>
  <pre>var SearchResults = require(&#x27;./SearchResults&#x27;);
</pre>
  <p>Это позволит нам использовать класс <strong>SearchResults</strong> изнутри класса <strong>SearchPage</strong>: </p>
  <p> Измените метод <strong>_handleResponse</strong>, заменив выражение <strong>console.log</strong> на следующее:</p>
  <pre>this.props.navigator.push({
  title: &#x27;Results&#x27;,
  component: SearchResults,
  passProps: {listings: response.listings}
});
</pre>
  <p>Этот код переходит к компоненту <strong>SearchResults</strong> и передает объекты недвижимости из API-запроса. Использование push-метода обеспечивает загрузку результатов поиска в стек переходов, вследствие чего у вас появится кнопка ‘Back’, чтобы вернуться в корневой каталог.</p>
  <p> Сохраните изменения, вернитесь в симулятор, нажмите Cmd+R и попробуйте выполнить поиск. Вы увидите список объектов недвижимости:</p>
  <figure class="m_original">
    <img src="https://habrastorage.org/files/c1f/b0e/3b2/c1fb0e3b205e4dad81dac2d8000e852c.png" width="281" />
  </figure>
  <p>Наконец-то мы получили настоящий список. Правда, выглядит он пока скучновато. Давайте немного преобразим его.</p>
  <p> <strong>Применяем стилизацию</strong></p>
  <p> Постепенно код React Native начинает приобретать знакомый вам вид, поэтому мы немного ускорим темп работы.</p>
  <p> Добавим данное определение стиля сразу после деструктурирующего присваивания в файле <strong>SearchResults.js</strong>:</p>
  <pre>var styles = StyleSheet.create({
  thumb: {
    width: 80,
    height: 80,
    marginRight: 10
  },
  textContainer: {
    flex: 1
  },
  separator: {
    height: 1,
    backgroundColor: &#x27;#dddddd&#x27;
  },
  price: {
    fontSize: 25,
    fontWeight: &#x27;bold&#x27;,
    color: &#x27;#48BBEC&#x27;
  },
  title: {
    fontSize: 20,
    color: &#x27;#656565&#x27;
  },
  rowContainer: {
    flexDirection: &#x27;row&#x27;,
    padding: 10
  }
});
</pre>
  <p>Здесь содержатся все стили для отображения каждого ряда данных.</p>
  <p> Заменим <strong>renderRow()</strong> следующим кодом:</p>
  <pre>renderRow(rowData, sectionID, rowID) {
  var price = rowData.price_formatted.split(&#x27; &#x27;)[0];
 
  return (
    &lt;TouchableHighlight onPress={() =&gt; this.rowPressed(rowData.guid)}
        underlayColor=&#x27;#dddddd&#x27;&gt;
      &lt;View&gt;
        &lt;View style={styles.rowContainer}&gt;
          &lt;Image style={styles.thumb} source={{ uri: rowData.img_url }} /&gt;
          &lt;View  style={styles.textContainer}&gt;
            &lt;Text style={styles.price}&gt;£{price}&lt;/Text&gt;
            &lt;Text style={styles.title}
                  numberOfLines={1}&gt;{rowData.title}&lt;/Text&gt;
          &lt;/View&gt;
        &lt;/View&gt;
        &lt;View style={styles.separator}/&gt;
      &lt;/View&gt;
    &lt;/TouchableHighlight&gt;
  );
}
</pre>
  <p>Этот код обрабатывает данные цен, полученных в формате ‘300,000 GBP’ и убирает оттуда GBP. Затем он отображает строку интерфейса, используя подход, с которым вы, скорее всего, уже хорошо знакомы. Изображение (<strong>Image</strong>) загружается из возвращенного URL (<strong>rowData.img_url</strong>), который React Native берет из основного потока, и добавляется в ряд.</p>
  <p> Также обратите внимание на использование стрелочной функции в свойстве <strong>onPress</strong> компонента <strong>TouchableHighlight</strong>. С ее помощью захватывается <strong>guid</strong> для ряда.</p>
  <p> Последний шаг – добавить этот метод в класс, чтобы управлять нажатием на экран:</p>
  <pre>rowPressed(propertyGuid) {
  var property = this.props.listings.filter(prop =&gt; prop.guid === propertyGuid)[0];
}
</pre>
  <p>Этот метод определяет, какой объект недвижимости был выбран пользователем. Правда, сейчас он не работает, но мы это скоро исправим. А пока любуйтесь результатом.</p>
  <p> Сохраните изменения, вернитесь в симулятор и нажмите Cmd+R, чтобы увидеть обновленный список:</p>
  <figure class="m_original">
    <img src="https://habrastorage.org/files/545/670/a1b/545670a1bd97447cbb76369e93f8834d.png" width="281" />
  </figure>
  <p>Выглядит отлично… если закрыть глаза на цены.</p>
  <p> Наконец, пришло время добавить последнюю страницу.</p>
  <p> <strong>Добавляем страницу просмотра информации о недвижимости</strong></p>
  <p> Создайте в проекте новый файл под названием <strong>PropertyView.js</strong> и введите туда этот код:</p>
  <pre>&#x27;use strict&#x27;;
 
var React = require(&#x27;react-native&#x27;);
var {
  StyleSheet,
  Image,
  View,
  Text,
  Component
} = React;
</pre>
  <p>Уверен, вы уже способны набрать его даже с закрытыми глазами. </p>
  <p> Теперь добавим стили:</p>
  <pre>var styles = StyleSheet.create({
  container: {
    marginTop: 65
  },
  heading: {
    backgroundColor: &#x27;#F8F8F8&#x27;,
  },
  separator: {
    height: 1,
    backgroundColor: &#x27;#DDDDDD&#x27;
  },
  image: {
    width: 400,
    height: 300
  },
  price: {
    fontSize: 25,
    fontWeight: &#x27;bold&#x27;,
    margin: 5,
    color: &#x27;#48BBEC&#x27;
  },
  title: {
    fontSize: 20,
    margin: 5,
    color: &#x27;#656565&#x27;
  },
  description: {
    fontSize: 18,
    margin: 5,
    color: &#x27;#656565&#x27;
  }
});
</pre>
  <p>И сам компонент:</p>
  <pre>class PropertyView extends Component {
 
  render() {
    var property = this.props.property;
    var stats = property.bedroom_number + &#x27; bed &#x27; + property.property_type;
    if (property.bathroom_number) {
      stats += &#x27;, &#x27; + property.bathroom_number + &#x27; &#x27; + (property.bathroom_number &gt; 1
        ? &#x27;bathrooms&#x27; : &#x27;bathroom&#x27;);
    }
 
    var price = property.price_formatted.split(&#x27; &#x27;)[0];
 
    return (
      &lt;View style={styles.container}&gt;
        &lt;Image style={styles.image}
            source={{uri: property.img_url}} /&gt;
        &lt;View style={styles.heading}&gt;
          &lt;Text style={styles.price}&gt;£{price}&lt;/Text&gt;
          &lt;Text style={styles.title}&gt;{property.title}&lt;/Text&gt;
          &lt;View style={styles.separator}/&gt;
        &lt;/View&gt;
        &lt;Text style={styles.description}&gt;{stats}&lt;/Text&gt;
        &lt;Text style={styles.description}&gt;{property.summary}&lt;/Text&gt;
      &lt;/View&gt;
    );
  }
}
</pre>
  <p>Часто случается так, что API возвращает данные низкого качества и с пропущенными полями. Потому первая часть <strong>render()</strong> проводит обработку данных для частичного улучшения их качества.</p>
  <p> Остальная его часть весьма очевидна: это функция неизменяемого состояния данного компонента.</p>
  <p> Теперь добавим экспорт в конец файла:</p>
  <pre>module.exports = PropertyView;
</pre>
  <p>Вернитесь к <strong>SearchResults.js</strong> и добавьте выражение <strong>require</strong> в начало файла, сразу после строки React <strong>require</strong>:</p>
  <pre>var PropertyView = require(&#x27;./PropertyView&#x27;);
</pre>
  <p>Затем обновите <strong>rowPressed()</strong>, чтобы перемещаться по <strong>PropertyView</strong>:</p>
  <pre>rowPressed(propertyGuid) {
  var property = this.props.listings.filter(prop =&gt; prop.guid === propertyGuid)[0];
 
  this.props.navigator.push({
    title: &quot;Property&quot;,
    component: PropertyView,
    passProps: {property: property}
  });
}
</pre>
  <p>Порядок действий вам знаком: сохраняем, возвращаемся в симулятор и нажимаем Cmd+R. Теперь можно выполнить поиск, выбрать любой объект и перейти к просмотру информации о нем:</p>
  <figure class="m_original">
    <img src="https://habrastorage.org/files/607/1f4/a4f/6071f4a4fd3241be8893d7c8f53ff639.png" width="281" />
  </figure>
  <p>Эх, вот что я называю доступным жильем!</p>
  <p> Приложение почти готово, осталось только добавить опцию поиска по геолокации. </p>
  <p> <strong>Реализуем поиск по местоположению</strong></p>
  <p> Откройте в Xcode файл Info.plist и добавьте напротив <strong>NSLocationWhenInUseUsageDescription</strong> следующее значение:</p>
  <pre>PropertyFinder would like to use your location to find nearby properties – 
</pre>
  <p>Вот как будет выглядеть ваш plist-файл после добавления нового значения:</p>
  <figure class="m_original">
    <img src="https://habrastorage.org/files/2ff/bd1/e40/2ffbd1e4076b4aa2abbb97a0af530538.png" width="480" />
  </figure>
  <p>Это информация, которую приложение будет отображать пользователям при попытке запросить их местоположение.</p>
  <p> Откройте SearchPage.js, перейдите к компоненту <strong>TouchableHighlight</strong>, отвечающему за отображение кнопки ‘Location’, и добавьте это значение:</p>
  <pre>onPress={this.onLocationPressed.bind(this)}
</pre>
  <p>По нажатию кнопки вызывается метод <strong>onLocationPressed</strong> (мы сейчас его добавим).</p>
  <p> Вставьте внутрь класса <strong>SearchPage</strong> следующий код:</p>
  <pre>onLocationPressed() {
  navigator.geolocation.getCurrentPosition(
    location =&gt; {
      var search = location.coords.latitude + &#x27;,&#x27; + location.coords.longitude;
      this.setState({ searchString: search });
      var query = urlForQueryAndPage(&#x27;centre_point&#x27;, search, 1);
      this._executeQuery(query);
    },
    error =&gt; {
      this.setState({
        message: &#x27;There was a problem with obtaining your location: &#x27; + error
      });
    });
}
</pre>
  <p>Данные о текущем местоположении берутся с помощью <strong>navigator.geolocation</strong>. Это <a href="https://developer.mozilla.org/en-US/docs/Web/API/Geolocation/Using_geolocation" target="_blank">интерфейс Web API</a>, потому он должен быть понятен каждому, кто имел дело с браузерными сервисами определения геолокации. React Native предоставляет собственную реализацию данного API, используя нативные средства iOS.</p>
  <p> В случае успешного определения текущего местоположения будет вызвана первая стрелочная функция. Она отправит запрос в Nestoria. Если же что-то пойдет не так, отобразится стандартное сообщение.</p>
  <p> Так как мы редактировали plist, потребуется перезапустить приложение, чтобы увидеть изменения. На этот раз, увы, без Cmd+R. Остановите приложение в Xcode и выполните его повторную сборку. Затем запустите проект.</p>
  <p> Прежде чем использовать поиск по геолокации, стоит убедиться, что база данных Nestoria содержит информацию о вашем регионе. Выберите в меню симулятора <strong>Debug\Location\Custom Location …</strong> и введите широту с долготой: к примеру, 55.02 и -1.42 соответственно. Это координаты одного живописного городка на побережье северной части Англии, откуда я родом.</p>
  <p> Теперь нажмите кнопку Location, разрешите приложению определять местоположение и смотрите результат.</p>
  <figure class="m_original">
    <img src="https://habrastorage.org/files/56f/dfb/810/56fdfb8102ab4433804417f0ada4270a.png" width="647" />
  </figure>
  <p><strong>Примечание от Рэя</strong>: Поиск по местоположению сработал не у всех. Как правило, возникала ошибка доступа, несмотря на то, что определение местоположения было разрешено. Мы пока не до конца разобрались с этой ситуацией. Возможно, это проблема самого React Native. Если кому-либо удалось исправить эту ошибку, пожалуйста, напишите нам.</p>

]]></content:encoded></item></channel></rss>