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

в 12:43, , рубрики: android development, react native, ReactJS, Разработка под 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

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

  • 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, дальше вам следует продолжить с этого, по идеи, все должно стать понятнее.
Ну я буду писать приложение для себя, по мере поступления и решения проблем напишу еще пару статей.

Автор: Simipa

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js