| сохранено

H React Native с колокольни Android-разработки

На хабре есть множество статей про react native, но я не могу понять кем и для кого они написаны. Почти все имеют такой посыл: «Ну, это такой же React как в вебе, только на мобилках». А вот люди, которые занимаются нативной разработкой под мобильные устройства в пролете, ведь разобраться в реакте после Android без двух литров без глубокого изучения никак не получится. А я расскажу за реакт опираясь, внезапно, не на веб разработку, а на Android. Всем заинтересованным добро пожаловать под кат.

Зачем?


Я понимаю, что основная аудитория react native это как раз таки веб разработчки, которые захотели потыкать в мобильные устройства. Но зачем мобильному разработчику смотреть в сторону react native? Я опущу момент как я вообще наткнулся на react native, скажу лишь то, что я уже несколько месяцев на него посматриваю и вот последние 4-5 дней активно изучаю. Я понял, что реакт уже готов что бы создавать простые приложения, которые принимают и отправляют данные. А потом я начал вспоминать все свои проекты и понял, что большая часть из них как раз принимает и отправляет данные, за редким исключением. Получается, что большую часть приложений я мог сделать реакте? Ага, а плюсом шло бы еще приложения на iOS.

Конечно, нативная разработка лучше по всем параметрам, но есть пару НО:

Во-первых, нативная разработка под андроид сложна и полна кучей мелочей, самый банальный пример — это поворот экрана, есть куча разных решений и библиотек, даже я сам нагородил свои костыли. А в реакте все просто, сделал экран, а про повороты можно забыть из коробки, все работает (как я понял) не в основном потоке, в основном только перерисовывается вью, поворот экрана жизненный цикл компонентов реакта никак не затрагивает и это прекрасно.

Смысл в том, что я занимаюсь нативной разработкой под android уже довольно долго и до сих пор приходится сталкиваться с новыми странными (но, порой, и интересными) проблемами. А если мне нужно приложения и под iOS, сколько уйдет времени что бы я научился нативно писать под iOS? Я уже изучал iOS и столкнулся с проблемой подходов: а как писать приложения? Паттерны, библиотеки, best practices и прочее. И для каждой платформы свои заморочки. В случае с реактом тоже свои проблемы, но они общие на обе платформы. Так что я свой выбор сделал: для простых приложений сил реакта хватает, когда начинаются трудности можно прибегнуть к главному козырю — написать модуль нативно под нужную платформу. Ну а вишенкой на торте становится то, что фраза: «Ну, это такой же React как в вебе, только на мобилках» работает и в обратную сторону, изучая react native я использую те же инструменты и подходы что и на вебе, можно будет намного легче сменить направление разработки на веб.

Инструменты


Непосредственно про установку можно почитать тут. Далее я буду считать, что вы прочитали тот мини-туториал.

Конечно, после Android studio все будет не так удобно, придется много работать с консолью (если не запускать студию вообще), но привыкнуть можно. Лично я использую:


Чтобы команда:

react-native run-android

возымела эффект необходимо, чтобы виртуальный девайс уже был запущен. Android studio все делает за нас после нажатия на волшебную зеленую кнопочку, а тут придется все делать вручную. Чтобы каждый раз не запускать студию ради запуска виртуального девайса просто открываем меню Android Virtual Device:

image

Запускаем нужный нам девайс и смотрим в консоль в студии, там будет что-то вроде:

/Users/spotlight1/Library/Android/sdk/tools/emulator -netdelay none -netspeed full -avd Nexus_5_API_22

Записываем куда надо, и вот у нас есть команда, с помощью которой мы можем запускать виртуальный девайс. Потом можно написать скриптик для запуска конкретного приложения.

Сразу добавлю полезную команду:

react-native log-android

Для того что бы видеть логи в терминале.

Компоненты


Я пока не стану останавливаться на том, как вообще заставить работать приложение, про это позже. Так же оговорю, что я буду использовать примеры от сюда, обязательно все там сами почитайте, но после этой статьи, по идеи, вникнуть станет гораздо проще, для этого и пишу статью.

Компонент является основой основ. Для начала стоит воспринимать компонент как Activity или Fragment (но фрагмент ближе). Хотя, компонентом может быть и 1 единственная кнопка. Но позже разберемся.

Самый простой компонент:

class Simple Component extends React.Component {
  render() {
    return (
      <Text>Hello world!</Text> //метод render() обязан быть в каждом компоненте, он отрисовывает вью
    );
  }
}

Этот компонент сделает нечто подобное:

image

Любой компонент имеет свой lifecycle, капитально почитать про это можно тут. Нас же интересует, в основном, только эти:

  • constructor() — сам говорит за себя, это конструктор компонента, это не обязательный метод, но позже я к нему вернусь;
  • render() — это метод почти аналог setContentView(R.layout.activity) из Android
  • componentDidMount() — это что-то вроде метода onCreate(), но немного по-другому, вызывается сразу после метода render()

componentDidMount() — это как раз то место, где нужно делать запросы к серверу, чтобы получить данные, которые необходимо отобразить. Он так же не является обязательным методом.

И все тут просто, но мы же захотим работать с данными? У компонента есть 2 источника данных:

  • Props
  • State

Props можно воспринимать как Intent, который мы передаем когда открываем другую активити. Это данные, которые передаются В компонент ИЗ вне.
А теперь наглядно:

class Greeting extends React.Component {
  render() {
    return (
      <Text>Hello {this.props.name}!</Text> //Вызываем полученный props
    );
  }
}

class LotsOfGreetings extends React.Component {
  render() {
    return (
      <View style={{alignItems: 'center'}}>
        <Greeting name='Rexxar' /> //Вызываем компонент и передаем props
        <Greeting name='Jaina' />
        <Greeting name='Valeera' />
      </View>
    );
  }
}

Получилось:

image

Давайте разбираться. Props вызывается внутри компонента конструкцией:

this.props.paramName

в этом примере параметр называется «name». Ну а в фигурных скобках нужно вызывать вообще все параметры, не только props.

Далее, нам необходимо передать props:

<Greeting name='Rexxar' />

Greeting — это имя нашего компонента что мы вызываем, а затем, через пробел мы передаем все props, в нашем случае это «name».

Да, необычно то, что мы явно не указываем в компоненте какие нам нужны props (или я что-то не до конца понял), а сразу же вызываем их в коде как будто они уже есть. Важно заметить, что props мы можем вызывать в любом месте компонента, не только в методе render(), а так же то, что props нельзя изменить, какие пришли, такие и останутся. Типичное применение — это передать id какого-то элемента из списка, на который мы тыкнули, чтобы загрузить какую-то информацию.

Теперь про state. State — это изменяемые параметры самого компонента. Их нельзя куда-то передать напрямую, так же нельзя получить из вне. С точки зрения Android это что-то вроде private параметров без сеттеров и геттеров. State инициализируются внутри конструктора компонента:


class Stopwatch extends React.Component {
  constructor(props) {
    super(props);
    this.state = {seconds: 0}; //Инициализация происходит только в конструкторе

    //Теперь каждую секунду значение seconds будет увеличиваться на 1
    setInterval(() => {
      this.setState({ seconds: (this.state.seconds +1) }); //Изменение происходит методом setState()
    }, 1000);

  render() {
    return (
      <Text>{this.state.seconds}</Text>
    );
  }
}

Вся прелесть в том, что изменение любого параметра state приводит к вызову метода render(). Т.е. этого кода достаточно чтобы мы получили реальный секундомер, счетчик будет обновляться самостоятельно.

Ну, собственно, про компоненты все. Как же работает само приложение? Все достаточно просто, есть две точки входа: index.is.js и index.android.js, понятно для кого которая. И вот эта строка является настоящей точкой входа:

AppRegistry.registerComponent('folderName', () => App);

«folderName» — это название корневой папки проекта, менять нельзя. App — это название компонента, который мы хотим использовать как главный. Своего рода это что-то вроде Main Activity.

Идеалогия написания кода


Когда я только начинал писать на реакт нейтив я стопорился на самых простых вопросах. Допустим, самый простой экран логина: 2 EditText и 1 кнопка. Вот уж не знаю почему, но я на уровне инстинкта пытался реализовать это как в ванильном Android, т.е. MVC без всяких библиотек: по нажатию кнопки берем значения этих EditText и отправляем куда надо, предварительно провалидировав их. Даже не пытайтесь мыслить таким образом.

Через некоторое время у меня в голове щелкнул какой-то тумблер и меня осенило — react native и data binding в Android — это почти одно и тоже (ну, не совсем). Однако, подход практически 1 в 1. Уже во второй своей статье я крайне рекомендую ознакомиться с data binding для андроида, если еще не. Иначе, эта статья вообще вам не поможет.

Для начала, посмотрим как выглядит xml на Android при использовании data binding:

<LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">
    <EditText
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:hint="Login"
              android:text="@={model.login}" />
    <EditText
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:hint="Password"
              android:text="@={model.password}" />
     <Button
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:onClick="@{() -> model.login()}" />
        
</LinearLayout>

А вот так это будет выглядеть на react native:


export default class LoginScreen extends React.Component {
	constructor(props) {
		super(props);
		this.state = {
			login: null, //Прямо как ObservableField в Android
			password: null
		};
	};

        //Метод логина
	login() {
	   if(this.state.login === null) {
                //тут мы получаем введенные данные
            }
	}

	render() {
	return(
		<View>
                   <TextInput
                        onChange={(event) => this.setState({ login: event.nativeEvent.text })} />
                   <TextInput
                        onChange={(event) => this.setState({ password: event.nativeEvent.text })} />
                   <Button
			 onPress={() => this.login()} //1 в 1 как в data binding
            		 title="Login"/>
                </View>
		);
	}
}

Ну и как вам такое? Лично у меня после этого отпали все вопросы по поводу того как писать под react native, тут мы пишем не в императивном стиле, а в реактивном. Сразу оговорюсь, что у компонента TextInput есть метод onChangeText, но у меня он почему-то не возвращал введенный текст.

Что дальше?


Я попытался осветить все моменты, которые у меня вызвали вопросы в момент изучения react native, дальше вам следует продолжить с этого, по идеи, все должно стать понятнее.
Ну я буду писать приложение для себя, по мере поступления и решения проблем напишу еще пару статей.
+24
~7600

комментарии (27)

0
Zo0m3R ,  
Не раскрыто с точки зрения Андроид-разработчика. Два (±) сравнения — мало.
Вот мне интересно, что можно использовать еще, кроме стандартных передачи-получения-обработки-показа данных. Те же нативные методы для работы с камерой, сетями, апи других процессов. Кое-что описано в доке, но хотелось бы узнать от тех, кто копнул глубже, как много ньюансов возникает в разработке полноценного приложения.
0
Simipa ,  
Копать глубже я буду и напишу еще статьи по этому поводу. Это же вводная статья для людей, которые хотели начать учить, но по каким-то причинам откладывали. Я объяснил самые азы, что бы люди уже самостоятельно смогли изучать. Лично для меня с первого подхода все этим props и state были не понятны.
–1
cheiwe ,  
Но, что бы понять, нужно ли тебе это и стоит ли учить, надо в целом понимать возможности приложения написанного на react. На основе вашей вводной статьи такого решения не принять. Следовательно, я скорее не буду больше тратить время как минимум на ваши статьи, а поищу более комплексный обзор + и -, который позволит мне сформировать примерную картину возможностей и определиться кидаться ли в этот омут или другой. Представляете, я вот ничего не знаю об этой теме и я не разработчик, честно сказать, но решил копнуть эту тему и сижу читаю вашу статью, прикидываю своё приложение, а потом в прекрасный момент пойму, что "***!" так я не смогу банально фотку получить с камеры ус-ва (например)!!! Ну и нафига я потратил столько времени? Тем более из вашей статьи можно сделать ошибочный вывод, что мы только и сможем, что гонять данные туда-сюда и всё.

Впрочем, это не умоляет того, что вы в принципе вложились в написание статьи. За это вам спасибо! А, всё, что выше небольшая критика от вашей ЦА, так сказать.
0
superyarik ,  
Интересный ракурс. Думаю стоит отметить, что запросы данных всё-таки нужно делать в сайд-эффектах, тут уже и про Redux нужно почитать.
0
Simipa ,  
Читаю про redux, но пока в своем тестовом проекте не знаю куда и зачем его прицепить. Потом, как закончу приложение, я сделаю еще 1 общий обзор с глубоким обзором react native, может быть и до redux доберусь. А если и не доберусь, то, надеюсь, люди подскажут где и зачем его использовать.
0
superyarik ,  
Если добавить ещё комментариев, которые по вашему мнению стоит знать реакт разработчикам, и которые скорее относятся к нативной части, то получится обзор на стыке технологий. Например рассказать про например папку android, что там за манифесты и вообще что можно там сделать из настроек нативного приложения. Я вот пока не понял как достучаться из реакта к ресурсам, положенным в android/app/src/main/assets/.
0
Simipa ,  
Хм, интересная идея, подумаю над этим. По поводу assets, я не понял, зачем это нужно, как я понял мы спокойно можем создать свою папку с картинками и прочим, импортировать их в js коде и оно будет работать что на андроид, что на iOS, разве не так?
0
comerc ,  

Есть прекрасный видео-курс для быстрого погружения в Redux — тынц


И еще вот это руководство перечитывал не один раз.

0
sergeyfitis ,  
React Native с колокольни Android-разработки

A на скриншотах iPhone :(


По сабжу, здесь JS интерпретируется или компилируется во что-то? И в какой момент времени это делается и как влияет на рантайм андроида?

0
Simipa ,  
Скриншоты я брал с оф. туториала react native, а там айфон(
По поводу работы: JS компилируется в obj-c для iOS и в java для андроида. С рантайм, естественно, страдает, смысл в том, что для андроида (iOS глубоко не изучал) есть 1 активити, которая и запускается при старте, и в эту активити потом отправляется скомпилированный JS код. Во всяком случае во время разработки (SDK я еще не собирал) происходил билд приложения, потом оно запускает эту активити, потом эта активити показывает процесс компиляции JS. Во время первого запуска рантайм увеличивается раза в 2. После, этот процесс намного быстрее. Как допишу приложение, расскажу как оно в готовом виде работает.
+3
Leopotam ,   * (был изменён)
Ничего там не компилируется — делается нейтивный враппер, работающий с UI и в который парсится весь JSX / TSX / порезанный flexbox (основной поток), а весь пользовательский код логики гоняется как есть (в виде JS) в фоновом потоке. Основное преимущество над phonegap и прочими решениями, основанными на полноэкранном webview — интерфейс таки нейтивный, а js-логика работает без блокировки основного потока.
0
igordata ,  
Отличная статья, спасибо!
0
+1 –1
Valery4 ,  
Чтобы было удобно так же как в AndroidStudio — рекомендую посмотреть на WebStorm, попробовать можно бесплатно.
+1
almazmusic ,   * (был изменён)
В ваших экспериментах есть ряд нюансов:
  1. state можно передавать, вглубь, но нельзя оттуда изменять. По сути передавая state верхнего компонента вглубь мы получаем некое подобие flux, когда всё приложение связано через единый store.
  2. установка state происходит не мгновенно, поэтому не нужно надеятся, что записав что-то в state произойдёт ре-рендер
    setState() does not always immediately update the component. It may batch or defer the update until later. This makes reading this.state right after calling setState() a potential pitfall. Instead, use componentDidUpdate or a setState callback (setState(updater, callback)), either of which are guaranteed to fire after the update has been applied. If you need to set the state based on the previous state, read about the updater argument below.
  3. вызов setState равносилен вызову метода render, но вы при этом лихо запихнули setState в render, на что обычно компилятор бы очень ругался ибо делать так нельзя.

В общем-то у вас очень альтернативный взгляд на всё это дело, что весьма интересно. Интересны такие взгляды на разного рода компоненты, вроде навигатора того же.
0
Simipa ,   * (был изменён)
Спасибо за объяснение работы state. Но, тогда у меня вопрос, как лучше сделать ту же страницу логина, если не записывать изменения в state?
0
almazmusic ,   * (был изменён)
Просто следовать идеологии Реакта. А именно по вашему примеру в Реакте терминология разделяет компоненты и контейнеры. Компоненты — это простые, маленькие и зачастую stateless-компоненты, в которых нет никакой логики. Контейнеры же собирают страницы и содержат всю логику, а также жизненные циклы (при этом контейнеры в целом это те же компоненты). В общем советую читать оригинальную доку. Она местами хоть и не очень, но вся идеология Реакта именно там.
0
almazmusic ,   * (был изменён)
Сори, конкретно по этому вопросу: да в этом и очень многих других примерах state будет работать как надо. Речь идёт о таких моментах, когда вы, например, изменили state и уже хотите опереться на эти изменения, например, в том же методе компонента — в таком случае state будет содержать прежнее состояние.
https://facebook.github.io/react/docs/react-component.html#setstate — вот детальное описание работы и нюансов setState.
0
vvadzim ,  
steState же вроде как в колбэках, а не в самом render. Норм. По-православному, конечно, для оптимизации, кобэки лучше в методы переделать, но это очень опционально.
0
almazmusic ,  
Ваша правда. Меньше сидеть до двух ночи и потом на хабре отвечать :)
0
comerc ,  

Пишите побольше о ReactNative!


Маленькая отсебятина. Можно существенно облегчить себе жизнь — быстрый старт на ReactNative.

0
jbo ,  
Очень интересен взгляд с другой стороны. Используйте сразу React+Redux, да писать лишнего кода больше (WebStorm вам поможет) но потов это с лихвой компенсируется.

Лучше так не писать что бы потом не говорили что react тормоз
<View style={{alignItems: 'center'}}>

а так
const style = {
      alignItems: 'center',
    };
....
render() {
  return (
    <View style={style}>
....
0
Simipa ,  
Да, я так и делаю, то был пример с оф. туториала react native =) Да и в любом случае, style обычно так сильно разрастается, что в любом случае приходится выводить отдельную константу под это.
+1
RifleR ,  

Маленькое замечание по коду — вы объявляете функции-обработчики событий прямо в props в функции рендеринга, так не рекомендуется делать (объяснение, почему именно, здесь: https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md ).

0
Simipa ,  
Спасибо, учту.
–1
3l1k ,  

Ощущение такое, что пишет десятиклассник, а не "андроид разработчик с многолетним стажем". В вашем тексте много грамматических и синтаксических ошибок. Авторитетность такой статьи под большим вопросом

0
Simipa ,  
Прощу прощения. По большей части это опечатки, т.к. пишу по ходу мысли, я не из тех кто долго-долго думает над текстом и пишет 33 черновика. Я пишу на вдохновении, а потом перечитываю пару раз свой текст. Конечно, проверяю и спел чекером, но от слова в не правильной форме это не спасает, а внимательно вчитываться в каждую букву на 3-4 раз уже невозможно. Буду работать над языком.
0
3l1k ,  

Пишите ещё! Затронули интересную и нужную тему