Перетяжка, Дом карьеры
Перетяжка, Дом карьеры
Перетяжка, Дом карьеры

Навигация в React Native: Искусство перемещения по экранам с помощью React Navigation

Разбираем, как организовать навигацию в React Native с помощью библиотеки React Navigation. Рассмотрим основные концепты, такие как стек и таб навигация, научимся перемещаться между экранами разных стеков и табов, а также разберём лучшие практики, хуки и подводные камни. Эта статья станет вашим путеводителем по созданию удобной и производительной системы навигации для мобильных приложений.

284 открытий3К показов
Навигация в React Native: Искусство перемещения по экранам с помощью React Navigation

Салют, народ! 👋

Если вы создаете мобильное приложение на React Native, то рано или поздно столкнётесь с необходимостью организовать навигацию между экранами. Сегодня мы разберём одну из самых популярных библиотек для этого — React Navigation . Это мощный инструмент, который позволяет создавать сложные и гибкие системы навигации. Погнали!

Что такое React Navigation?

React Navigation — библиотека, которая помогает управлять переходами между экранами в React Native приложении и не только. Она предоставляет готовые компоненты и API для создания различных типов навигации: стек, таб, боковой навигации и даже модальных окон.

Основная идея заключается в том, что навигация представляет собой дерево , где каждый узел — экран или группа экранов. Это дерево можно строить рекурсивно, добавляя новые уровни навигации внутри уже существующих.

Как это работает?

React Navigation использует концепцию навигаторов (navigators). Навигатор — контейнер, который управляет группой экранов. Вот основные типы навигаторов:

  1. StackNavigator — стек навигация, где экраны накладываются друг на друга.
  2. TabNavigator — таб навигация, где экраны переключаются через нижнюю или верхнюю панель.
  3. DrawerNavigator — выдвижная боковая панель для навигации.
  4. BottomSheetNavigator — модальные экраны, которые выдвигаются снизу.

Каждый навигатор может содержать другие навигаторы, формируя древовидную структуру. Например, у тебя может быть TabNavigator, внутри которого есть StackNavigator для каждого таба. Листьями будут конечные экраны приложения.

Структура навигации как дерево

Структура навигации — фундамент вашего приложения. От того, насколько грамотно она организована, зависит не только удобство пользователей, но и простота поддержки кода, производительность и масштабируемость проекта.

Навигация должна быть организована логично. Например:

			AppNavigator
  ├── AuthNavigator (StackNavigator)
  │     ├── LoginScreen
  │     └── RegisterScreen
  └── MainNavigator (TabNavigator)
        ├── HomeStack (StackNavigator)
        │     ├── HomeScreen
        │     └── DetailsScreen
        ├── ProfileScreen
        └── SettingsScreen
		

Здесь AppNavigator — корневой навигатор, который содержит два дочерних навигатора: AuthNavigator и MainNavigator. В свою очередь, MainNavigator содержит HomeStack, который уже управляет экранами HomeScreen и DetailsScreen.

Такая структура позволяет:

  1. Логически разделить функциональность: Например, экраны авторизации можно отделить от основной части приложения.
  2. Управлять состоянием: Каждый навигатор хранит своё состояние (текущий экран, параметры переходов и т.д.), что делает управление предсказуемым.
  3. Оптимизировать рендеринг: React Navigation автоматически оптимизирует рендеринг экранов, загружая только те, которые находятся в текущем фокусе.

Как строится навигация?

Корневой навигатор

Корневой навигатор — первый уровень дерева. Обычно он используется для разделения логики авторизации и основного приложения. Пример:

			import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';

const RootStack = createStackNavigator();

function App() {
  return (
    <NavigationContainer>
      <RootStack.Navigator>
        {/* Если пользователь не авторизован */}
        <RootStack.Screen name="Auth" component={AuthNavigator} />
        {/* Если пользователь авторизован */}
        <RootStack.Screen name="Main" component={MainNavigator} />
      </RootStack.Navigator>
    </NavigationContainer>
  );
}
		

Здесь AuthNavigator и MainNavigator — отдельные компоненты, которые содержат свои собственные навигаторы.

Навигаторы внутри навигаторов

Как видно из примера выше, навигаторы могут быть вложенными. Это позволяет создавать сложные системы навигации. Например, MainNavigator может быть TabNavigator, а внутри одного из табов — StackNavigator.

			import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { createStackNavigator } from '@react-navigation/stack';

const Tab = createBottomTabNavigator();
const Stack = createStackNavigator();

function HomeStack() {
  return (
    <Stack.Navigator>
      <Stack.Screen name="Home" component={HomeScreen} />
      <Stack.Screen name="Details" component={DetailsScreen} />
    </Stack.Navigator>
  );
}

function MainTabs() {
  return (
    <Tab.Navigator>
      <Tab.Screen name="Home" component={HomeStack} />
      <Tab.Screen name="Profile" component={ProfileScreen} />
      <Tab.Screen name="Settings" component={SettingsScreen} />
    </Tab.Navigator>
  );
}
		

Здесь HomeStack — стековая навигация, которая находится внутри MainTabs.

Логика авторизации

Один из самых распространённых подходов — разделение навигации на две части: для неавторизованных и авторизованных пользователей. Это можно сделать с помощью глобального состояния (например, Redux или Context API).

Пример:

			function App() {
  const isAuthenticated = useAuth(); // Предположим, что это хук для проверки авторизации

  return (
    <NavigationContainer>
      {isAuthenticated ? <MainNavigator /> : <AuthNavigator />}
    </NavigationContainer>
  );
}
		

Такой подход позволяет легко переключаться между двумя состояниями приложения.

Таб навигация

Таб навигация — один из самых популярных способов организации интерфейса. Вот пример простой реализации:

			import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';

const Tab = createBottomTabNavigator();

function MainTabs() {
  return (
    <Tab.Navigator
      screenOptions={({ route }) => ({
        tabBarIcon: ({ focused, color, size }) => {
          let iconName;

          switch(route.name) {
            case 'Home':
              iconName = focused ? 'home' : 'home-outline';
              break;
            case 'Profile':
              iconName = focused ? 'person' : 'person-outline';
              break;
          }

          return <Ionicons name={iconName} size={size} color={color} />;
        },
      })}
    >
      <Tab.Screen name="Home" component={HomeScreen} />
      <Tab.Screen name="Profile" component={ProfileScreen} />
    </Tab.Navigator>
  );
}
		

Совет: используйте библиотеку react-native-vector-icons для иконок. Они делают интерфейс более привлекательным.

Почему такая структура?

Разделение ответственности

Каждый навигатор отвечает за свою часть приложения. Например:

  • AuthNavigator управляет экранами входа и регистрации,
  • MainNavigator управляет основным функционалом приложения.

Это упрощает поддержку кода и делает его более читаемым.

Управление состоянием

Каждый навигатор хранит своё состояние. Например, если пользователь находится на экране Details внутри HomeStack, то при возвращении на главный экран Home, состояние будет сохранено.

Гибкость

Вложенная структура позволяет комбинировать различные типы навигации. Например, вы можете использовать TabNavigator для основных экранов и StackNavigator для деталей внутри каждого таба.

Почему не стоит использовать стандартный header?

Стандартный header библиотеки часто выглядит слишком сырым, плохо кастомится и неочевидно работает. Лучше скрывать его и реализовывать собственный компонент для заголовков. Это даёт больше контроля над дизайном и функциональностью:

			<Stack.Navigator
  screenOptions={{
    headerShown: false,
  }}
>
  {/* Экраны */}
</Stack.Navigator>
		

Deeplink при помощи навигации

Deeplink позволяет открывать конкретные экраны приложения через ссылки. Например, если пользователь кликнет на ссылку myapp://profile, он сразу попадёт на экран профиля.

Для реализации deeplink используйте метод linking в корневом навигаторе:

			const linking = {
  prefixes: ['myapp://'],
  config: {
    screens: {
      Home: 'home',
      Profile: 'profile',
    },
  },
};

<NavigationContainer linking={linking}>
  {/* Навигаторы */}
</NavigationContainer>
		

Более подробно про deeplink рассказывал тут.

Хуки React Navigation

React Navigation предоставляет множество полезных хуков, например:

  • useNavigation — получение объекта навигации,
  • useRoute — доступ к параметрам текущего маршрута,
  • useIsFocused — проверка, находится ли экран в фокусе.

Пример использования:

			import { useNavigation } from '@react-navigation/native';

function MyComponent() {
  const navigation = useNavigation();

  return (
    <Button
      title="Go to Details"
      onPress={() => navigation.navigate('Details')}
    />
  );
}
		

Про хуки можно рассказывать много и долго, лучше этот момент изучить в документации. Но есть несколько советов относительно их использования:

  1. Не злоупотребляйте useNavigation. Если вы используете useNavigation слишком часто, это может быть признаком того, что ваша архитектура требует пересмотра. Постарайтесь минимизировать его использование.
  2. Избегайте лишних ререндеров. Хуки, такие как useIsFocused или useFocusEffect, могут вызывать дополнительные ререндеры. Используйте React.memo или useCallback для оптимизации.
  3. Очистка эффектов. Не забывайте очищать эффекты в useFocusEffect, чтобы избежать утечек памяти.
  4. Проверяйте типы данных. При работе с useRoute всегда проверяйте, что параметры существуют, чтобы избежать ошибок.

Подводные камни и нюансы

  1. Передача параметров между экранами. Не забывайте очищать параметры, если они больше не нужны. Иначе это может привести к утечкам памяти.
  2. Глубокое дерево навигации. Слишком сложная структура навигации может затруднить отладку и поддержку кода. Старайтесь держать её максимально простой.
  3. Анимации. По умолчанию анимации могут быть тяжёлыми для производительности. Используйте react-native-reanimated для оптимизации.
  4. Обновление состояния. При использовании глобального состояния (например, Redux) убедитесь, что оно обновляется корректно при переходах между экранами.

Вспомогательные библиотеки

Для улучшения работы с React Navigation полезны следующие библиотеки:

  1. react-navigation-shared-element — анимации переходов между экранами,
  2. react-navigation-hooks — хуки для удобной работы с навигацией,
  3. react-native-screens — оптимизация производительности экранов.

Как лучше всего построить структуру: советы

Минимизируйте вложенность

Как уже упоминал ранее, слишком глубокая вложенность усложняет отладку и может привести к проблемам с производительностью. Старайтесь держать структуру максимально плоской.

Используйте ленивую загрузку

Ленивая загрузка экранов помогает уменьшить время загрузки приложения. Например:

			const LazyScreen = React.lazy(() => import('./screens/SomeScreen'));

<Stack.Screen
  name="Lazy"
  component={LazyScreen}
/>
		

Избегайте дублирования

Если у вас есть повторяющиеся элементы интерфейса (например, header), вынесите их в отдельные компоненты, чтобы избежать дублирования кода.

Используйте глобальные параметры

Для передачи данных между экранами используйте параметры маршрута (route.params). Однако избегайте передачи больших объёмов данных через параметры — лучше используйте глобальное состояние.

Пример полной структуры

			import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';

const RootStack = createStackNavigator();
const Tab = createBottomTabNavigator();
const HomeStack = createStackNavigator();

function HomeScreens() {
  return (
    <HomeStack.Navigator>
      <HomeStack.Screen name="Home" component={HomeScreen} />
      <HomeStack.Screen name="Details" component={DetailsScreen} />
    </HomeStack.Navigator>
  );
}

function MainTabs() {
  return (
    <Tab.Navigator>
      <Tab.Screen name="Home" component={HomeScreens} />
      <Tab.Screen name="Profile" component={ProfileScreen} />
      <Tab.Screen name="Settings" component={SettingsScreen} />
    </Tab.Navigator>
  );
}

function App() {
  const isAuthenticated = useAuth();

  return (
    <NavigationContainer>
      <RootStack.Navigator>
        {!isAuthenticated ? (
          <RootStack.Screen name="Auth" component={AuthNavigator} />
        ) : (
          <RootStack.Screen name="Main" component={MainTabs} />
        )}
      </RootStack.Navigator>
    </NavigationContainer>
  );
}
		
Правильная структура навигации — ключ к созданию удобного и производительного приложения. React Navigation предоставляет мощные инструменты для организации, но важно подходить к этому процессу осознанно. Разделяйте логику, минимизируйте вложенность и следуйте лучшим практикам, чтобы ваше приложение было не только красивым, но и удобным для поддержки.

Если остались вопросы или хотите обсудить конкретные случаи — пишите в комментариях!

Следите за новыми постами
Следите за новыми постами по любимым темам
284 открытий3К показов