Создания приложения на React Native: инструкция
Расскажу вам про простую разработку приложения на React Native. Сейчас это намного проще чем было много лет назад.
В качестве примера покажу вам моё приложение, которое я разработал и уже загрузил в Google Play. Это простое приложение для изучения сербского языка. Вот оно:
Установка и запуск React Native
Итак, что же нужно сейчас чтобы создать приложение. На самом деле для React разработчика всё очень просто, не нужно даже особо вникать, как работать с операционными системами и при этом можно писать свой код только на JS для React Native.
Чтобы избежать этого, нужно использовать EXPO. Он как раз и позволяет это делать. У него в наборе много полезных инструментов и пакетов, а также он собирает для вас само приложение, без всяких дополнительных утилит для вашего ПК.
Создаем новый проект одним скриптом (можно добавить на JS или TS):
npx create-expo-app --template
Выбираем Navigation (TypeScript)
, чтобы не настраивать routing в дальнейшем
После этого наше пустое приложение создалось, можно его запускать.
npx expo start
После запуска мы увидим в консоли QR-код и IP по которому мы увидем наше приложение в браузере. Для начала вам этого хватит, но лучше запускать его в телефоне, что очень просто.
Для этого нам нужно установить приложение EXPO Go. Запустить его и отсканировать наш QR-код из консоли. Важно чтобы ваш телефон и ПК были в одной локальной сети (под одним вайфаем например)
С запуском разобрались.
Разработка на React Native
Сейчас я вам расскажу, какой инструментарий я выбрал для моего приложения. Начнем с наиболее важных.
Роутинг
Роутинг - навигация всегда нужна, так как одной страничкой не отделаешься, я взял expo-router
, который работает на базе стандартного @react-navigation/native
.
Если вы выбрали Navigation (TypeScript)
при создании приложения, то у вас уже все настроено, если нет, то вот инструкция
По дефолту у вас будет две странички. Главная c табами и модальное окно.
В файле app/_layout
у нас будет две странички
<Stack>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen name="modal" options={{ presentation: 'modal' }} />
</Stack>
И в папке app/(tabs)/_layout
у нас ещё две подстраницы или табы. На базе этих примеров не трудно разобраться
Оформление
Так с нуля верстать не особо легко по началу для мобилок, проще всего взять готовый UI фреймворк. Я решил не брать самые популярные на подобе Bootstrap, а взял не особо заметную, но красивую либу - UI Kitten. Как оказалось, там не все было, что мне нужно, но большую часть потребностей закрыла.
Устанавливается просто:
npm i @ui-kitten/components @eva-design/eva react-native-svg
И подключем в файле app/_layout
либу и иконки:
import { useColorScheme } from 'react-native';
import { ApplicationProvider, IconRegistry } from '@ui-kitten/components';
import { EvaIconsPack } from '@ui-kitten/eva-icons';
function RootLayoutNav() {
const colorScheme = useColorScheme();
const theme = colorScheme === 'dark' ? material.dark : material.light;
return (
<ApplicationProvider {...material} theme={theme}>
<IconRegistry icons={EvaIconsPack} />
<Stack>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen name="modal/index" options={{ presentation: 'modal' }} />
</Stack>
</ApplicationProvider>
);
}
У нас тут есть поддержка светлой и темной темы, если не нужно, то выберите в theme
темную material.dark
или светлую material.light
тему
Ну и пример вызова Input с иконкой:
import { Icon, Input } from '@ui-kitten/components';
<Input accessoryRight={<Icon name='search-outline'/>} />
Хранилище данных
С роутингом и дизайном разобрались и ещё нужно нам хранить какие-то данные пользователя, например у меня в приложении я храню выбранный язык, выбранную тему (светлую, темную, как в системе) и сербские слова, которые пользователь уже выучил.
Для этого я взял самую популярную либу Async Storage, которое хранит данные в приложении. И также я связал его со state-медежром, взял я конечно популярный Redux-Toolkit, который часто используют для обычного React. Ну и чтобы связать Redux-Toolit и Async Storage, использовал Redux-Persist.
В принципе можно обойтись и одним Async Storage, но без стейт медежера мало что получится, так как хочется подписываться на данные и реагировать на них, ну и стучаться из любой страницы.
Давайте подключим это всё дело. Установим пакеты:
npm i react-redux redux-persist @reduxjs/toolkit @react-native-async-storage/async-storage @types/react-redux
Теперь создадим главный файл для хранилища, например store/index.ts
.
import {configureStore, combineReducers} from '@reduxjs/toolkit';
import {persistReducer, persistStore} from 'redux-persist';
import AsyncStorage from '@react-native-async-storage/async-storage';
import {userReducer} from './slices/userSlice.ts';
const persistConfig = {
storage: AsyncStorage,
key: 'root',
};
const rootReducer = combineReducers({
userReducer,
});
export const persistedReducer = persistReducer(persistConfig, rootReducer);
export const store = configureStore({
reducer: persistedReducer,
middleware: (getDefaultMiddleware) => getDefaultMiddleware({
serializableCheck: false,
}),
});
export const persistor = persistStore(store);
export type RootState = ReturnType;
export type AppDispatch = typeof store.dispatch;
Для TypeScript необходимо типизировать хуки для select и dispatch (для получения и записи данных). Создадим здесь store/hooks.ts
:
import { useDispatch, useSelector } from 'react-redux';
import type { TypedUseSelectorHook } from 'react-redux';
import type { RootState, AppDispatch } from './store';
export const useAppDispatch: () => AppDispatch = useDispatch;
export const useAppSelector: TypedUseSelectorHook = useSelector;
Далее создадим slice для данных пользователя (slice = action + reducer) в store/slices/userReducer.ts
.
import { createSlice, type PayloadAction } from '@reduxjs/toolkit';
import { type TStoreWords, type TTheme } from '../../constants/types';
import { type RootState } from '../store';
interface IUserStore {
data: {
settings: {
theme: 'dark' | 'light';
lang: 'ru' | 'en'
};
};
}
const initialState: IUserProps = {
data: {
settings: {
theme: 'light',
lang: 'ru',
},
},
};
const userSlice = createSlice({
name: 'user',
initialState,
reducers: {
changeLang: (state, action: PayloadAction) => {
state.data.settings.lang = action.payload;
},
changeTheme: (state, action: PayloadAction) => {
state.data.settings.theme = action.payload;
},
},
});
export const selectTheme = (state: RootState) => {
return state.user.data.settings.theme;
};
export const { changeLang, changeTheme } = userSlice.actions;
export const userReducer = userSlice.reducer;
Здесь в качестве примера уже есть экшены и селектор, этот файл мы уже подключили в стор выше.
Осталось только лишь подключить наше хранилище, вернемся к файлу app/_layout
:
import { useColorScheme } from 'react-native';
import { ApplicationProvider, IconRegistry } from '@ui-kitten/components';
import { EvaIconsPack } from '@ui-kitten/eva-icons';
import { Provider } from 'react-redux';
import { PersistGate } from 'redux-persist/integration/react';
function RootLayoutNav() {
const colorScheme = useColorScheme();
const theme = colorScheme === 'dark' ? material.dark : material.light;
return (
<Provider store={store}>
<PersistGate persistor={persistor}>
<ApplicationProvider {...material} theme={theme}>
<IconRegistry icons={EvaIconsPack} />
<Stack>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen name="modal/index" options={{ presentation: 'modal' }} />
</Stack>
</ApplicationProvider>
</PersistGate>
</Provider>
);
}
Использовать очень просто, вот так например в ваших компонентах: (не стал добавлять полный пример, это для наглядности)
import { useAppDispatch, useAppSelector } from '../../store/hooks';
import { changeTheme, selectTheme } from '../../store/slices/userSlice';
function SomeComponent(){
// подключаем диспатчер для записи в стор
const dispatch = useAppDispatch();
// получаем текущую тему
const theme = useAppSelector(selectTheme);
const handleChangeTheme = (lang: IUserStore['data']['settings']['theme']) => {4
// меняем язык в store
dispatch(changeTheme(lang));
}
return (
<Select
label={'Изменить тему'}
onSelect={handleChange}
>
{['en', 'ru'].map(item => (
<SelectItem title={item} key={item} />
))}
</Select>
);
}
Это основные моменты, которые я считаю обязательно должны быть в вашем приложении.
Сборка и публикация приложения
Так как мы используем EXPO, нам не нужно компилировать приложения у себя на ПК, это будем делать через сайт EXPO, мы будем запускать команды. Для этого нам нужно будет завести у них аккаунт.
В бесплатной версии 50 сборок в месяц, чего мне хватило, иногда бывают очереди, приходится ждать по часу, но для меня тоже не проблема.
Первый скрипт, сгенерит вам .apk
файл, я это использую, чтобы устоновить и потыкать готовое приложение:
Установим глобально пакет для запуска скриптов
npm install -g eas-cli
Генерация apk
:
eas build -p android --profile preview
После запуска, он вам предложит ссылку, по которой мы будем видеть, как собирается наше приложения, через какое-то время, можно будет его скачать.
Если хотите публиковать приложение в Google Play, то вам нужен будет .aab
файл, он генерится аналогично через скрипт:
eas build -p android --profile production
После этого, можно будет публиковать его в Google Play.
Кратко про публикации приложения
Чтобы опубликовать приложения в Google Play и AppStore, вам нужно будет завести аккаунт разработчика. После этого, вы сможете выкладывать любое количество приложений, но это не бесплатно. Вот что по ценам:
- Google Play - разово 25$
- AppStore - ежегодно 99$
Я своё приложения опубликовал только в Google Play, так как это стоит недорого и оплатить надо только один раз.
Нужно заполнить заявку, отправить документы, заплатить членский взнос. После этого подождать пару дней и как правило все будет ок. Не уверен, что сейчас можно будет это делать с документами РФ, проверяйте информацию, все меняется.
И после этого можно будет загрузить свой aab файл и увидеть свое приложение в Google Play.
Правда там много всего, что нужно будет заполнить и Google не сразу разрешит вам это сделать, но разобраться можно, информации полно и я это сделал. Всем удачи!.
Оставить комментарий