Pull to refresh

Первый взгляд на react-native

Reading time 5 min
Views 24K
Если вы фронтенд разработчик, да еще и с опытом работы 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 специалистов.

Ссылка на репозиторий
Tags:
Hubs:
+10
Comments 7
Comments Comments 7

Articles