Вы проснулись. Сияет солнце, щебечут птички. В мире никто ни с кем не воюет, никто не голодает, а один и тот же код можно использовать и в веб-проектах, и в нативных приложениях. Как бы было хорошо! К сожалению, на горизонте можно разглядеть лишь универсальный код, но путь к нему, даже сегодня, всё ещё полон неожиданностей.
Материал, перевод которого мы сегодня публикуем, представляет собой небольшое, но достаточно подробное руководство по разработке универсальных приложений с использованием React Native.
<div>Здравия желаю! Стандартный веб-контейнер к вашим услугам!</div>
<View>Привет! Я - простой контейнер в React Native</View>
Geolocation
, Platform
, Animated
, AsyncStorage
и многие другие. Взгляните на замечательные примеры, которые можно найти в руководстве к этой библиотеке.create-react-app rnw_web
. Затем создал второй проект: react-native init raw_native
. Затем я, последовав примеру Виктора Франкенштейна, взял файлы package.json
из этих двух проектов и объединил их. После этого я, в папке нового проекта, скормил новый файл yarn
. Вот о каком package
-файле идёт речь:{
"name": "rnw_boilerplate",
"version": "0.1.0",
"private": true,
"dependencies": {
"react": "^16.5.1",
"react-art": "^16.5.1",
"react-dom": "^16.5.1",
"react-native": "0.56.0",
"react-native-web": "^0.9.0",
"react-navigation": "^2.17.0",
"react-router-dom": "^4.3.1",
"react-router-modal": "^1.4.2"
},
"devDependencies": {
"babel-jest": "^23.4.0",
"babel-preset-react-native": "^5",
"jest": "^23.4.1",
"react-scripts": "1.1.5",
"react-test-renderer": "^16.3.1"
},
"scripts": {
"start": "node node_modules/react-native/local-cli/cli.js start",
"test": "jest",
"start-ios": "react-native run-ios",
"start-web": "react-scripts start",
"build": "react-scripts build",
"test-web": "react-scripts test --env=jsdom",
"eject-web": "react-scripts eject"
}
}
src
, которая лежит в директории нового проекта, создадим два файла: App.js
и App.native.js
. Благодаря webpack мы можем использовать расширения имён файлов для того, чтобы сообщить бандлеру о том, где какие файлы нужно использовать. Разделить App
-файлы жизненно важно, так как мы собираемся использовать разные подходы для навигации по приложениям.App.js
, предназначенный для веба. Для навигации используется react-router
.// App.js - WEB
import React, { Component } from "react";
import { View } from "react-native";
import WebRoutesGenerator from "./NativeWebRouteWrapper/index";
import { ModalContainer } from "react-router-modal";
import HomeScreen from "./HomeScreen";
import TopNav from "./TopNav";
import SecondScreen from "./SecondScreen";
import UserScreen from "./UserScreen";
import DasModalScreen from "./DasModalScreen";
const routeMap = {
Home: {
component: HomeScreen,
path: "/",
exact: true
},
Second: {
component: SecondScreen,
path: "/second"
},
User: {
component: UserScreen,
path: "/user/:name?",
exact: true
},
DasModal: {
component: DasModalScreen,
path: "*/dasmodal",
modal: true
}
};
class App extends Component {
render() {
return (
<View>
<TopNav />
{WebRoutesGenerator({ routeMap })}
<ModalContainer />
</View>
);
}
}
export default App;
App.js
для React Native-приложения. Тут для навигации используется react-navigation
.// App.js - React Native
import React, { Component } from "react";
import {
createStackNavigator,
createBottomTabNavigator
} from "react-navigation";
import HomeScreen from "./HomeScreen";
import DasModalScreen from "./DasModalScreen";
import SecondScreen from "./SecondScreen";
import UserScreen from "./UserScreen";
const HomeStack = createStackNavigator({
Home: { screen: HomeScreen, navigationOptions: { title: "Home" } }
});
const SecondStack = createStackNavigator({
Second: { screen: SecondScreen, navigationOptions: { title: "Second" } },
User: { screen: UserScreen, navigationOptions: { title: "User" } }
});
const TabNav = createBottomTabNavigator({
Home: HomeStack,
SecondStack: SecondStack
});
const RootStack = createStackNavigator(
{
Main: TabNav,
DasModal: DasModalScreen
},
{
mode: "modal",
headerMode: "none"
}
);
class App extends Component {
render() {
return <RootStack />;
}
}
export default App;
import WebRoutesGenerator from "./NativeWebRouteWrapper"; // функция собственной разработки, которая генерирует маршруты React Router и оборачивает их в HOC
const routeMap = {
Home: {
screen: HomeScreen,
path: '/',
exact: true
},
Menu: {
screen: MenuScreen,
path: '/menu/sectionIndex?'
}
}
//в методе render
<View>
{WebRoutesGenerator({ routeMap })}
</View>
react-router
и оборачиваю их в HOC. Это позволяет клонировать компонент screen
и добавить navigation
в его свойства. Этот подход имитирует поведение React Navigation и делает доступными методы вроде navigate()
, goBack()
, getParam()
.createStackNavigator
, даёт возможность сделать так, чтобы некая страница приложения выезжала бы снизу в виде модального экрана. Для того чтобы добиться подобного в веб-приложении, мне пришлось использовать библиотеку React Router Modal. Для работы с модальным экраном сначала надо добавить соответствующую опцию в объект routeMap
:const routeMap = {
Modal: {
screen: ModalScreen,
path: '*/modal',
modal: true //маршрутизатор будет использовать компонент ModalRoute для рендеринга этого маршрута
}
}
<ModalContainer />
из библиотеки react-router-modal
. Выводиться соответствующая страница будет именно там.NativeWebRouteWrapper
?, и это, кстати, ужасное имя), мы можем использовать практически тот же набор функций, что и в React Navigation, для организации перемещения между страницами в веб-версии приложения:const { product, navigation } = this.props
<Button
onPress={navigation.navigate('ProductScreen', {id: product.id})}
title={`Go to ${product.name}`}
/>
<Button
onPress={navigation.goBack}
title="Go Back"
/>
n
экранов, которые находятся в навигационном стеке. Подобное в React Router недоступно, тут нет навигационного стека. Для того чтобы решить эту проблему, нам нужно импортировать в код функцию pop
собственной разработки. Вызывая её, передадим ей несколько параметров:import pop from '/NativeWebRouteWrapper/pop'
render() {
const { navigation } = this.props
return (
<Button
onPress={pop({screen: 'FirstScreen', n: 2, navigation})}
title="Go back two screens"
/>
)
}
screen
— имя экрана (используемое React Router в веб-версии приложения).n
— число экранов для возврата с использованием стека (используется React Navigation).navigation
— объект, обеспечивающий навигацию.К сожалению, не доступен сервер mySQL