Все для создания и продвижения сайтов

Верстка, программирование, SEO

Создания приложения на 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 не сразу разрешит вам это сделать, но разобраться можно, информации полно и я это сделал. Всем удачи!.

Выделите опечатку и нажмите Ctrl + Enter, чтобы отправить сообщение об ошибке.