Урок по треку React Native 6 мин

Навигация: React Navigation и Expo Router

React Navigation: стек, вкладки, параметры, deep links, Expo Router.

В React Native нет встроенного роутера. Стандарт де-факто это React Navigation, а Expo Router построен поверх него и добавляет маршрутизацию по файлам. На собесе спрашивают виды навигаторов, передачу параметров, вложенность, авторизацию и диплинки.

Навигаторы

  • Native Stack (@react-navigation/native-stack): стек экранов на нативных примитивах (UINavigationController, фрагменты). Нативные жесты и анимации, хорошая производительность.
  • Stack (@react-navigation/stack): стек на JS, гибко настраиваемые анимации, но больше работы на JS-потоке.
  • Bottom Tabs: нижние вкладки, у каждой может быть свой стек.
  • Drawer: боковое меню.
const Stack = createNativeStackNavigator<RootStackParamList>();

<NavigationContainer>
  <Stack.Navigator>
    <Stack.Screen name="Home" component={HomeScreen} />
    <Stack.Screen name="Item" component={ItemScreen} />
  </Stack.Navigator>
</NavigationContainer>

С седьмой версии есть и статический API (createStaticNavigation), где конфигурация описывается объектом, и типы с диплинками выводятся автоматически.

Переходы и параметры

navigation.navigate('Item', { id: '42' });
navigation.push('Item', { id: '43' }); // ещё один экран поверх, даже того же
navigation.goBack();
navigation.replace('Home');
navigation.popToTop();

const { id } = route.params;
  • push всегда добавляет новый экран. navigate может переиспользовать уже открытый экран с тем же именем; детали этого поведения менялись между версиями, а для явного возврата к экрану из стека в седьмой версии есть popTo.
  • В параметры передают сериализуемые данные: id, а не объекты и функции. Иначе ломаются диплинки, восстановление состояния и появляется предупреждение о несериализуемых значениях.
  • Типизация: RootStackParamList описывает параметры каждого экрана, NativeStackScreenProps<RootStackParamList, 'Item'> даёт типы route и navigation.

Жизненный цикл экрана

Экраны стека не размонтируются, когда сверху открыли новый: предыдущий остаётся в памяти. Поэтому useEffect с пустыми зависимостями не сработает при возврате. Для этого есть useFocusEffect (выполняется при каждом фокусе экрана) и useIsFocused. Подписки, которые нужны только на видимом экране (камера, таймер), запускают в useFocusEffect и чистят в его функции очистки.

Авторизация

Рекомендуемый подход: условный рендер экранов, а не ручные переходы.

<Stack.Navigator>
  {user ? (
    <Stack.Screen name="Main" component={MainTabs} />
  ) : (
    <Stack.Screen name="Login" component={LoginScreen} />
  )}
</Stack.Navigator>

Вошёл пользователь, и экраны входа исчезли из дерева: кнопка «назад» на них не вернёт. В статическом API то же делается через if у групп экранов.

Deep links

linking в NavigationContainer сопоставляет адреса и экраны:

const linking = {
  prefixes: ['myapp://', 'https://shop.example'],
  config: { screens: { Item: 'item/:id' } },
};

Нативная часть: своя схема (myapp://) и проверенные https-ссылки (App Links на Android, Universal Links на iOS). Холодный старт по ссылке обрабатывается через начальный URL, тёплый через событие url.

Expo Router

Маршруты задаются файлами: app/index.tsx, app/item/[id].tsx, группы (tabs), общий макет в _layout.tsx. Диплинки и типизированные маршруты получаются автоматически, потому что у каждого экрана есть адрес. Под капотом всё тот же React Navigation.

Частые ошибки

  • Объекты и функции в параметрах.
  • Ждать размонтирования предыдущего экрана и повторного useEffect.
  • Ручной navigate('Login') при выходе вместо условного рендера.
  • Тяжёлые экраны в JS Stack вместо Native Stack.

Что спросят на собесе

  • Native Stack против JS Stack.
  • navigate против push, как передать и типизировать параметры.
  • Почему useEffect не срабатывает при возврате на экран.
  • Как устроить поток авторизации.
  • Как настроить диплинки, что даёт Expo Router.

Закрепите за 5 минут

После урока в тренажёре 3-5 проверочных вопросов, дальше модуль уходит в повторения.