Год назад у меня появилось желание попробовать эту технологию и я начал писать приложение-словарь, которое позволяет сохранять категорию английских слов с их переводом. Версия 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 и другие). Давайте рассмотрим разработку компонента на примере создания карточки для отображения категории слов.

?Ниже представлена 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)
Frimko
12.01.2018 20:172ой взгляд мало кто делает))
Если какого-то функционала нет, всегда можно найти библиотеку в сообществе react-native специалистов.
К сожалениию не всегда, будьте готовы изучать нативный язык устройства. Тот же OAuth vkSKd и odnoklassniki для react-native(далее RN) как таковые рабочие пакеты отсутствуют. Либо используйте более старую версию RN, либо допиливайте ручками(зачастую у некоторых пакетов нативного кода, даже примеры без напильника не запускаются). Если вы хотите что-то серьезного, помимо стандартной веб-морды, то RN не для вас.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&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
k12th
13.01.2018 01:48create-reactive-native-app на данный момент создает т.н. «Expo-приложение» (это какая-то третье-сторонняя контора). Из плюшек — какое-то количество своих кросс-платформенных API, из коробки работает SVG, можно опубликовать свое приложение без возни со сторами (но оно будет доступно только в этом самом Expo). Из минусов — оверхед в 25 мегов (без него — всего 8), эмуляторы и девайсы по USB недоступны (ну или как-то хорошо спрятаны), на устройство надо ставить приложение Expo и оно должно быть в той же WiFi-сети.
react-native init MyAppName
создает вполне рабочий проект без черной магии, запускающийся в эмуле.
vtvz_ru
13.01.2018 01:48Хочу заметить, что React Native очень хорошо работает с async/await. И тем образом код становится очень аккуратным и легко читаемым. Правда, отловить забытый await порой бывает очень сложно. Вроде как даже перед такими функциями, как componentWillMount, можно ставить async
Terras
13.01.2018 12:20Реакт убог и его использование на адекватном уровне — невозможно. Пробовали одно время на нем делать приложения, в итоге тупо столкнулись с тем, что все время идут какие-то косяки на уровне телефонов, чаще всего китайских и самсунгов, либо в пограничных ситуаций, которых, когда у тебя 100 000 + пользователей — каждый день валится по 200-300.
В итоге, решили не страдать фигней, и писали на нативных языках под каждую платформу.vtvz_ru
13.01.2018 22:16Зато он невероятно удобен для pet проектов. У меня где-то с десяток небольших приложений, написанных за пару дней, для меня и моей семьи.
Про серьёзные приложения ничего сказать не могу, так как это не мой род деятельности. Но мне, как веб разработчику, очень нравится подобная технология из-за знакомого мне JavaScript.
alltiptop
Тоже как то создал проект в познавательных целях, запустил и первая функция что попробовал сделать оказалась недоступна из стандартного api (шаринг в моё приложение, а не из него), а для того чтобы она заработала предлагали только создавать нативное приложение с интеграцией реакта внутри, в итоге из маленького приложения для отправки файла 90% работы платформозависимы. В больших проектах это будет капля в море и оно имеет смысл, но как то неприятно столкнуться с проблемами в таком расхваленном фреймворке на 5 минуте знакомства с ним.