Если вы фронтенд разработчик, да еще и с опытом работы react + redux, то вам определенно стоит обратить внимание на такой интересный инструмент как react-native. Он позволяет разрабатывать кроссплатформенные приложения под ios и android. React-native находится в стадии активного развития и выпускает обновления каждый месяц.

Год назад у меня появилось желание попробовать эту технологию и я начал писать приложение-словарь, которое позволяет сохранять категорию английских слов с их переводом. Версия react-native на тот момент была 0.37, теперь версия 0.52. Несколько недель назад я решил возобновить разработку и столкнулся со стандартной проблемой быстрорастущих проектов, а именно довольно частая и кардинальная смена функциональности. Мне пришлось переписать некоторые вещи для того чтобы приложение запустилось. Вначале мне это сильно не понравилось, однако дальше я понял что эти изменения увеличивают скорость и качество разработки приложения. Дальше я бы хотел вкратце рассмотреть основные моменты разработки на react-native, c которыми я столкнулся в процессе.

На данный момент вы можете создать приложение 2 способами: с помощью create-react-native-app и react-native init. Create-react-native-app — это npm пакет, который позволяет вам создать начальную структуру приложения и запустить его на смартфоне без установки окружения для каждой из платформ. Однако если вам понадобится добавить в приложение нативный код или подключить библиотеку, которая это делает, то вам все равно придется устанавливать окружение.

И вот у вас уже есть готовый проект, а что же делать дальше? Ведь у нас нет ни CSS, ни HTML. Зато у нас есть jsx синтаксис, а также синтаксис для стилизации, очень похожий на inline стили в html. Для верстки макета используется flexbox такой же, как и на веб. В react-native нет привычных для фронтенд разработчика HTML элементов, вместо этого есть react-native компоненты для верстки, описание которых можно найти на официальном сайте. Есть кроссплатформенные компоненты(View, Button, TextInput), а также платформозависимые (DatePickerIOS, ProgressBarAndroid и другие). Давайте рассмотрим разработку компонента на примере создания карточки для отображения категории слов.

image

?Ниже представлена jsx разметка для данного компонента.

<View style={[styles.card, customStyle]} elevation={5}>
    <TouchableNativeFeedback
        onPress={() => onCardBodyClick(categoryId, categoryName)}
        background={TouchableNativeFeedback.Ripple('black')}
    >
        <View style={styles.cardBody}>
            <Text style={styles.bodyText}>{categoryName}</Text>
        </View>
    </TouchableNativeFeedback>

    <View style={styles.cardActions}>
        <ColoredFlatButton onPress={() => onRemove(categoryId)}>
            <Icon size={18} name="close" color="grey" />
        </ColoredFlatButton>
        <ColoredFlatButton onPress={() => onEdit(categoryId)}>
            <Icon size={18} name="mode-edit" color="grey" />
        </ColoredFlatButton>
        <ColoredFlatButton onPress={() => onStudy(categoryId)}>
            <Icon size={18} name="school" color="grey" />
        </ColoredFlatButton>
    </View>
</View>

View компонент похож на div в вебе и является одним из основных при создании компонента. TouchableNativeFeedback это компонент который позволяет обрабатывать нажатие на вложенный в него элемент. ColoredFlatButton и Icon компоненты из библиотеки react-native-material-kit. Как видим из примера выше верстка в react-native ничем не отличается от верстки в react, за исключением того, что используются компоненты из react-native вместо HTML элементов.

Дальше мы рассмотрим стилизацию этого компонента.

const styles = StyleSheet.create({
   card: {
       marginTop: 10,
       width: 160,
       height: 140,
       justifyContent: 'flex-end',
   },
   cardBody: {
       flex: 1,
       padding: 16,
       justifyContent: 'flex-end',
   },
   bodyText: {
       fontSize: 18,
       color: 'white',
   },
   cardActions: {
       padding: 8,
       flexDirection: 'row',
       backgroundColor: 'white',
       justifyContent: 'space-between',
       alignItems: 'flex-end',
   },
});

Для того чтобы создать стили нужно импортировать класс StyleSheet из react-native и передать ему объект стилей. Для применения стиля к элементу нужно указать его в атрибуте style.

<View style={styles.cardActions}>

Я думаю что в стилях мы не будем разбираться, человеку знакомому с css и так вcе понятно. Единственное отличие, так это то что размеры указываются не в CSS единицах измерения, а в Density-independent Pixels. Это единицы измерения которые позволяют приложению выглядеть одинаково на различных экранах и разрешениях в IOS и Android.
После того как в приложении появляется больше чем одна страница стоит задуматься как сделать переход между ними. До недавних пор добавить навигацию в приложение было достаточно сложно.

Приведу пример как это делалось раньше.

const _navigator = null;
class EnglishApp extends Component {
   onNavBackPress = () => {
       _navigator.pop();
   };
   renderScene = (route, navigator) => {
       _navigator = navigator;
       switch (route.id) {
           case routeIDs.NEW_WORD:
               return <SingleWordScreen navigator={navigator} onNavIconClicked={this.onNavBackPress} />;
           case routeIDs.WORD_LIST:
               return <WordListScreen navigator={navigator} onNavIconClicked={this.onNavBackPress} />;
       }
   };
   render() {
       return (
           <Navigator
               initialRoute={routeIDs.CATEGORY}
               renderScene={this.renderScene}
               configureScene={(route, routeStack) => Navigator.SceneConfigs.FloatFromRight}
           />
       );
   }
}

Согласитесь, выглядит не очень? Теперь ситуация изменилась, появилось несколько пакетов, которые рекомендуются в документации(native-navigation, react-native-navigation, react-navigation). Я использовал react-navigation. Все оказалось просто, достаточно импортировать navigator и указать настройки.

const RootNavigator = DrawerNavigator(
   {
       [RoutesID.CATEGORY]: {
           screen: CategoryScreen,
           navigationOptions: {
               drawerLabel: 'Категории',
               drawerIcon: ({ tintColor }) => (
                   <Icon name="local-library" color={tintColor} size={22} />
               ),
           },
       },
       [RoutesID.NEW_WORD]: {
           screen: NewWordScreen,
           navigationOptions: {
               drawerLabel: () => null,
           },
       },
   },
   {
       drawerWidth: 250,
       drawerPosition: 'left',
       contentOptions: {
           inactiveTintColor: 'darkgray',
       },
       drawerOpenRoute: 'DrawerOpen',
       drawerCloseRoute: 'DrawerClose',
       drawerToggleRoute: 'DrawerToggle',
   },
);

Первым параметром передаются маршруты приложения, а также можно указать настройки для header каждой страницы. Вторым параметром передаются настройки для компонента Drawer — это меню которое открывается слева по нажатию на бургер иконку. Есть возможность интеграции с redux.

После того как в приложении появилась навигация и несколько экранов, стоит задумать о сохранении данных. Если приложение не использует соединение с интернетом, тогда нужно хранить данные на устройстве. Для этого у нас есть SQLite. Для работы с бд я использовал пакет react-native-sqlite-storage. Немного повозился с установкой, а проблема оказалось очевидной, после добавления в проект этой библиотеки нужно было переустановить приложение на устройстве. Я использовал метод при котором в проекте уже есть база данных, которая используется при установке приложения на устройство, как это сделать описано на странице модуля в github. Для установки соединения нужна всего лишь одна строка.

open() {
    return SQLite.openDatabase({ name: 'englishAppDB.db', createFromLocation: 1 }).then(
        (db) => {
            this.db = db;

            Promise.resolve();
        },
        error => Promise.reject(error),
    );
}

А также простой пример запроса к базе данных.


    getAllCategories() {
        return this.db.executeSql('SELECT * FROM category', []).then(
            ([{ rows }]) => {
                let category = new List();

                for (let i = 0; i < rows.length; i += 1) {
                    category = category.push(new CategoryRecord({
                        id: rows.item(i).Id,
                        name: rows.item(i).name,
                        color: rows.item(i).color,
                    }));
                }

                return category;
            },
            error => Promise.reject(error),
        );
    }

В заключение могу сказать что после использования react-native я остался доволен данной технологией. Удобная отладка в браузере, никакого отличия от отладки веб приложений. Если какого-то функционала нет, всегда можно найти библиотеку в сообществе react-native специалистов.

Ссылка на репозиторий

Комментарии (7)


  1. alltiptop
    12.01.2018 20:14

    Тоже как то создал проект в познавательных целях, запустил и первая функция что попробовал сделать оказалась недоступна из стандартного api (шаринг в моё приложение, а не из него), а для того чтобы она заработала предлагали только создавать нативное приложение с интеграцией реакта внутри, в итоге из маленького приложения для отправки файла 90% работы платформозависимы. В больших проектах это будет капля в море и оно имеет смысл, но как то неприятно столкнуться с проблемами в таком расхваленном фреймворке на 5 минуте знакомства с ним.


  1. Frimko
    12.01.2018 20:17

    2ой взгляд мало кто делает))

    Если какого-то функционала нет, всегда можно найти библиотеку в сообществе react-native специалистов.

    К сожалениию не всегда, будьте готовы изучать нативный язык устройства. Тот же OAuth vkSKd и odnoklassniki для react-native(далее RN) как таковые рабочие пакеты отсутствуют. Либо используйте более старую версию RN, либо допиливайте ручками(зачастую у некоторых пакетов нативного кода, даже примеры без напильника не запускаются). Если вы хотите что-то серьезного, помимо стандартной веб-морды, то RN не для вас.


    1. FasSsko
      12.01.2018 20:56

      Мы последний год используем RN в продакшене. Приложения у нас гибридные, часть на RN, часть нативная, так вот под RN кастомное почти ничего не писали, только Bridge для общения RN — Native, всё остальное есть в npm. По поводу перфоманса, RN не совсем хорошо справляется с большим количеством анимаций, а так в целом не отличить где RN а где Native. В общем мы большем выигрыше с тех пор, как начали использовать RN, ибо приложение делается сразу на две платформы без особых усилий.
      Парочку ссылок на апки, для тех кому интересно:
      X Factor iOS: itunes.apple.com/gb/app/the-x-factor-uk/id455682741?mt=8
      X Factor Android: play.google.com/store/apps/details?id=com.tellybug.xfactor&amp;hl=en_GB
      Love Island iOS: itunes.apple.com/gb/app/love-island/id994494368?mt=8
      Love Island Android: play.google.com/store/apps/details?id=com.itv.tellybug.loveisland


  1. k12th
    13.01.2018 01:48

    create-reactive-native-app на данный момент создает т.н. «Expo-приложение» (это какая-то третье-сторонняя контора). Из плюшек — какое-то количество своих кросс-платформенных API, из коробки работает SVG, можно опубликовать свое приложение без возни со сторами (но оно будет доступно только в этом самом Expo). Из минусов — оверхед в 25 мегов (без него — всего 8), эмуляторы и девайсы по USB недоступны (ну или как-то хорошо спрятаны), на устройство надо ставить приложение Expo и оно должно быть в той же WiFi-сети.


    react-native init MyAppName создает вполне рабочий проект без черной магии, запускающийся в эмуле.


  1. vtvz_ru
    13.01.2018 01:48

    Хочу заметить, что React Native очень хорошо работает с async/await. И тем образом код становится очень аккуратным и легко читаемым. Правда, отловить забытый await порой бывает очень сложно. Вроде как даже перед такими функциями, как componentWillMount, можно ставить async


  1. Terras
    13.01.2018 12:20

    Реакт убог и его использование на адекватном уровне — невозможно. Пробовали одно время на нем делать приложения, в итоге тупо столкнулись с тем, что все время идут какие-то косяки на уровне телефонов, чаще всего китайских и самсунгов, либо в пограничных ситуаций, которых, когда у тебя 100 000 + пользователей — каждый день валится по 200-300.

    В итоге, решили не страдать фигней, и писали на нативных языках под каждую платформу.


    1. vtvz_ru
      13.01.2018 22:16

      Зато он невероятно удобен для pet проектов. У меня где-то с десяток небольших приложений, написанных за пару дней, для меня и моей семьи.
      Про серьёзные приложения ничего сказать не могу, так как это не мой род деятельности. Но мне, как веб разработчику, очень нравится подобная технология из-за знакомого мне JavaScript.