Компоненты навигации RN Routing-React 5.x — основные принципы (китайская документация)

React Native

##введение React Нативная маршрутная навигация, этого достаточно! Этот документ основан наReact NavigationПеревод документа, некоторый контент будет объяснен в соответствии с вашим собственным пониманием и не будет копировать дословный перевод.Если вы обнаружите какие-либо проблемы с вашим пониманием, вы можете указать на это! Так как я базируюсь на разработке под iOS, версию под Android еще не практиковал, буду практиковать позже, если будет время.Если возникнут проблемы, можете @me. Наконец, вот краткое изложение проблем, возникающих при работе на iOS, и решения. Наконец, поскольку статья в этом фильме будет очень длинной, я рекомендую плагин для Chrome, который может автоматически генерировать каталог в соответствии с h1~h6 в статье, что удобно для просмотра содержимого главы, а также может быть используется при написании статей!Smart TOC, нажмите «Установить», как показано ниже:

截屏2020-06-0810.27.10.png

Фундаментальный

1 запуск

Если вы уже знакомы с React Native, то сможете быстро освоиться с React Navigation!Если нет, то вам нужно сначала прочитать его.React Native ExpressЧасти 1–4 (включая часть 4) из , и вернитесь сюда, когда закончите.

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

1.1 Установка

Установите нужные вам пакеты в проекте RN

  • npm
npm install @react-navigation/native
  • yarn
yarn add @react-navigation/native

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

Теперь нам нужно установитьreact-native-gesture-handler,react-native-reanimated,react-native-screens,react-native-safe-area-context,@react-native-community/masked-viewЭти библиотеки, если вы установили последние версии этих библиотек, вы можете пропустить следующее, в противном случае продолжайте чтение.

1.2 Установите зависимости наExpoУправление проектами

cd в каталог вашего проекта и запустите:

expo install react-native-gesture-handler react-native-reanimated react-native-screens react-native-safe-area-context @react-native-community/masked-view

Эта команда установит наиболее подходящие версии этих библиотек. Далее можно переходить к написанию кода в проекте. (Примечание: использование Expo для управления проектами еще не использовалось. Если у вас есть сомнения по поводу детской обуви, спросите сами!)

1.3 Установка зависимостей в собственные проекты RN

cd в каталог вашего проекта и запустите:

  • npm
npm install react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-view
  • yarn
yarn add react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-view

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

Начало RN версии 0.60 или выше, эти библиотеки будут автоматически связаны элементами, поэтому не нужно управлять реактивным звеном.

Если вы разрабатываете проект iOS на Mac, вам необходимо установить Cocoapods, чтобы завершить ссылку на элемент:

npx pod-install ios

Когда вы завершили установку react-native-gesture-handler, импортируйте react-native-gesture-handler в файл входа проекта (например, index.js или App.js) (убедитесь, что это первая строка входной файл)

import 'react-native-gesture-handler';

Примечание. Если вы проигнорируете этот шаг, произойдет сбой в рабочей среде, хотя в разработке он будет работать нормально.

Теперь нам нужно загрузить все приложение в NavigationContainer. Обычно это делается в файле ввода (например, index.js или App.js):

import 'react-native-gesture-handler';
import * as React from 'react';
import { NavigationContainer } from '@react-navigation/native';

export default function App() {
  return (
    <NavigationContainer>{/* Rest of your app code */}</NavigationContainer>
  );
}

Примечание. При использовании навигатора, такого как навигатор стека, для любых других зависимостей необходимо следовать инструкциям по установке навигатора. Если вы получаете ошибку «Невозможно разрешить модуль», вам необходимо установить компонент, указанный в ошибке, в ваш проект.

Теперь вы можете скомпилировать и запустить проект на устройстве или эмуляторе и продолжить кодирование.

1.4 Возникшие проблемы

报错:TypeError: null is not an object (evaluating '_RNGestureHandlerModule.default.Direction')

Решение:Добавьте в файл Podfile в папке ios:

pod 'RNGestureHandler', :path => "../node_modules/react-native-gesture-handler"

Команда терминала cd в файл ios, запустите pod install

2 Hello React Navigation

В веб-браузере вы можете использоватьТег ссылается на другую страницу. Когда пользователь щелкает ссылку, URL-адрес помещается в стек истории браузера, а когда пользователь нажимает кнопку «Назад», браузер выводит ранее посещенную страницу из стека истории как текущую отображаемую страницу. В React Native нет встроенной концепции глобального стека истории, как в веб-браузерах — это то, что делает React Navigation.

Навигатор стека React Navigation позволяет приложениям переходить между экранами и управлять историей переходов. Если ваше приложение использует только один навигатор стека, это концептуально похоже на то, как веб-браузер обрабатывает состояние навигации — когда пользователь взаимодействует с ним, ваше приложение выталкивает и извлекает элементы из стека навигации, и пользователь может перейти на другую страницу. . Ключевое различие в том, как это работает в веб-браузерах и React Navigation, заключается в том, что навигатор стека React Navigation предоставляет жесты и анимацию, необходимые для навигации по маршрутам в стеке в Android и iOS.

Начнем с самого распространенного навигатора презентаций, createStackNavigator.

2.1 Установите библиотеку навигатора стека

На данный момент мы установили библиотеки, которые являются строительными блоками и общей базой навигаторов, каждый навигатор в React Navigation находится в своей собственной библиотеке. Чтобы использовать навигатор стека, нам нужно установить @response-navigation/stack:

  • npm
npm install @react-navigation/stack
  • yarn
yarn add @react-navigation/stack

напоминать:@react-navigation/stackзависит от нашегоНачинатьГлава Установленные библиотеки@react-native-community/masked-view, если вы еще не установили его, вернитесь к предыдущей главе.

2.2 Создание навигатора стека

createStackNavigator — это функция, которая возвращает объект с двумя свойствами: screen и navigator. Оба они являются компонентами React, используемыми для настройки навигатора. Навигатор должен иметь элемент экрана в качестве дочернего элемента для определения конфигурации маршрута.

NavigationContainer — это компонент, который управляет деревом навигации и содержит состояние навигации. Компонент должен обернуть все структуры навигатора. Как правило, мы визуализируем этот компонент в корне приложения, который обычно является компонентом, экспортируемым из app.js. Здесь я настроил файл маршрутизации, а затем добавил его в app.js:

//自定义一个路由文件NavigationComponent.js
import React from 'react';
import {Text, View} from 'react-native';
import {NavigationContainer} from '@react-navigation/native';
import {createStackNavigator} from '@react-navigation/stack';

function HomeScreen({navigation}) {
  return (
    <View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
      <Text>Home Screen</Text>
    </View>
  );
}

const Stack = createStackNavigator();

function NavigationComponent() {
  return (
    <NavigationContainer>
      <Stack.Navigator initialRouteName="Home">
        <Stack.Screen name="Home" component={HomeScreen} />
      </Stack.Navigator>
    </NavigationContainer>
  );
}

export default NavigationComponent;
//在App.js中引入
import 'react-native-gesture-handler';
import React, {Component} from 'react';
import NavigationComponent from './Sections/常用组件/NavigationComponent';

export default class App extends Component {
  render() {
    return <NavigationComponent />;
  }
}

Попробуйте написать на Snack

Simulator Screen Shot - iPhone 11 - 2020-06-08 at 15.03.00.png
Если вы запустите этот код, вы увидите экран (показан выше) с пустой панелью навигации и серой областью содержимого, содержащей основные компоненты экрана. Стили, которые вы видите для панели навигации и области содержимого, являются конфигурацией навигатора стека по умолчанию, мы узнаем, как их настроить позже.

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

Единственная конфигурация, необходимая для экрана, — это имя и свойства компонента. ты сможешьstack navigator referenceУзнайте больше о других доступных параметрах в .

2.3 Настройка навигатора

Все конфигурации маршрутизации указываются как реквизиты навигатора. Мы не передавали никакие реквизиты навигатору, поэтому он просто использует конфигурацию по умолчанию.

Давайте добавим второй экран в навигатор стека и настроим основной экран для отображения первым:

function HomeScreen() {...}

function DetailsScreen() {
  return (
    <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
      <Text>Details Screen</Text>
    </View>
  );
}

const Stack = createStackNavigator();

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

Попробуйте написать на Snack

Теперь в нашем стеке есть две страницы маршрутизации, основная страница и страница сведений. Маршруты можно указать с помощью компонента Screen. Компонент экрана принимает свойство имени, соответствующее имени маршрута для навигации, и свойство компонента, соответствующее компоненту, который он будет отображать.

Здесь основной маршрут соответствует сборке HomeScreen, а также подробную информацию о маршруте, соответствующую сборке экрана деталей. Начальный стек маршрутизации является основным маршрутом. Попробуйте изменить его к деталям и перезагрузить приложение (как вы могли бы ожидать, отреагируйте нативное быстрое обновление. Изменения не обновляются INALITROUTENAME), обратите внимание, что теперь вы увидите экран деталей. Затем измените его домой и перезагрузите снова.

注意:组件prop只接受component,而不是渲染函数。不要传递内联函数(例如component={() => }),否则当父组件重新渲染时,你的组件将会卸载和重新加载并移除所有的state。 ВидетьPassing additional propsзаменить.

2.4 Укажите параметры

Для каждого экрана в навигаторе можно указать некоторые параметры, например заголовок отображаемой страницы. Эти параметры можно передать в свойства параметров каждого компонента экрана:

<Stack.Screen
  name="Home"
  component={HomeScreen}
  options={{ title: 'Overview' }}
/>

Попробуйте написать на Snack

Иногда мы хотим указать одинаковые параметры для всех экранов в навигаторе. Для этого мы можем передать свойство screenOptions навигатору.

2.5 Прохождение реквизита

Иногда мы можем захотеть передать реквизиты на экран. Мы можем сделать это двумя способами:

1. ИспользуйтеReact contextПоставщик оборачивает навигатор для передачи данных на экран (рекомендуется).

2. Используйте обратные вызовы рендеринга экрана вместо указания реквизита компонента:

<Stack.Screen name="Home">
  {props => <HomeScreen {...props} extraData={someData} />}
</Stack.Screen>

Примечание. По умолчанию React Navigation оптимизирует компоненты экрана, чтобы предотвратить ненужный рендеринг. Использование обратных вызовов рендеринга удаляет эти оптимизации. Поэтому, если вы используете обратные вызовы рендеринга, вам нужно использовать React.memo или React.PureComponent для компонента, чтобы избежать проблем с производительностью.

2.6 Что дальше?

Следующий вопрос: «Как мне перейти с главной страницы на страницу сведений?», который будет рассмотрен в следующем разделе.

2.7 Резюме

  • В React Native нет встроенного API навигации, как в веб-браузерах. React Navigation предоставляет вам эту функциональность, а также жесты и анимацию для iOS и Android для переключения между экранами.
  • Stack.Navigator — это компонент, который настраивает маршруты как дочерние компоненты и свойства для конфигурации, а также отображает содержимое.
  • Каждый компонент Stack.Screen принимает свойство имени, которое ссылается на имя маршрута, а свойство компонента указывается как компонент, отображаемый маршрутом. Вот 2 необходимых реквизита.
  • Чтобы указать начальный маршрут в стеке, установите initialRouteName в качестве реквизита навигатора.
  • Чтобы указать параметры, специфичные для экрана, мы можем передать свойство options в стек. Для общих параметров мы можем передать параметры экрана в Stack.Navigator.

3 экрана переключения страниц

В предыдущих главах мы определили маршрут стека, который содержит главную страницу и страницу сведений, но не узнали, как разрешить пользователю перемещаться по главной странице и странице сведений (хотя мы узнали, как изменить начальный маршрут в коде, но позволили пользователь принудительно клонирует библиотеку и меняет начальный маршрут, чтобы показать другую страницу, что является наихудшим вообразимым пользовательским опытом).

В случае веб-браузера мы можем сделать это:

<a href="details.html">Go to Details</a>

Другой способ:

<a
  onClick={() => {
    window.location.href = 'details.html';
  }}
>
  Go to Details
</a>

Мы сделаем что-то похожее на глобальную переменную window.location, которую мы передадим компоненту экрана с помощью свойства навигации.

3.1 Переход на новую страницу

import React from 'react';
import { Button, View, Text } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';

function HomeScreen({ navigation }) {
  return (
    <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
      <Text>Home Screen</Text>
      <Button
        title="Go to Details"
        onPress={() => navigation.navigate('Details')}
      />
    </View>
  );
}

// ... other code from the previous section

Попробуйте написать на Snack

Давайте проанализируем это:

  • navigation — в навигаторах стека свойство навигации передается каждому компоненту экрана (определение)середина. позже«Детализация свойств навигации»Подробно описано в .
  • navigation('Details') — мы используем имя маршрута для вызова функции навигации, чтобы добраться до страницы, которую хочет увидеть пользователь (в свойстве навигации — назвать сложно!).

Если мы используем navigation.navigate для перехода к имени маршрута, которое не определено в навигаторе стека, ничего не произойдет. Другими словами, мы можем перемещаться только по маршрутам, определенным в навигаторе стека, а не к произвольным компонентам. Теперь у нас есть два маршрута в стеке: (1) основной маршрут, (2) подробный маршрут. Что произойдет, если мы снова перейдем на страницу сведений со страницы сведений?

function DetailsScreen({ navigation }) {
  return (
    <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
      <Text>Details Screen</Text>
      <Button
        title="Go to Details... again"
        onPress={() => navigation.navigate('Details')}
      />
    </View>
  );
}

Попробуйте написать на Snack

Если вы запустите этот код, когда вы снова нажмете «Перейти к деталям...», ничего не будет сделано! Потому что мы уже на странице сведений.

Предположим, мы хотим перейти на новую страницу сведений. Это очень распространено при передаче некоторых уникальных данных на каждый маршрут (подробнее об этом позже при обсуждении параметров!). Для этого мы можем использовать нажатие навигации, что позволяет добавлять маршрут, не беспокоясь о том, есть ли маршрут в стеке.

<Button
  title="Go to Details... again"
  onPress={() => navigation.push('Details')}
/>

Попробуйте написать на Snack

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

3.2 Возврат

В роутинге в заголовке текущей страницы будет кнопка «Назад».Нажав кнопку «Назад», можно вернуться на предыдущую страницу (но если в роутинге только одна страница, в шапке нет кнопки «Назад», а кнопка «назад» не работает).

Иногда вы хотите сделать это, написав код, мы используемnavigation.goBack();Сюда:

function DetailsScreen({ navigation }) {
  return (
    <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
      <Text>Details Screen</Text>
      <Button
        title="Go to Details... again"
        onPress={() => navigation.push('Details')}
      />
      <Button title="Go to Home" onPress={() => navigation.navigate('Home')} />
      <Button title="Go back" onPress={() => navigation.goBack()} />
    </View>
  );
}

Попробуйте написать на Snack

На Android React Navigation возвращает то же событие, когда пользователь нажимает аппаратную кнопку «Назад». Другим распространенным требованием является возврат нескольких страниц — например, вы перешли на несколько страниц и хотите сразу перейти к первой странице, в этом случае мы хотим вернуться на главную, поэтому мы можем использовать navigation(' Home') (вместо толчка! Попробуйте и увидите разницу). Другой вариант — navigation.popToTop(), который возвращает на первый экран в стеке.

function DetailsScreen({ navigation }) {
  return (
    <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
      <Text>Details Screen</Text>
      <Button
        title="Go to Details... again"
        onPress={() => navigation.push('Details')}
      />
      <Button title="Go to Home" onPress={() => navigation.navigate('Home')} />
      <Button title="Go back" onPress={() => navigation.goBack()} />
      <Button
        title="Go back to first screen in stack"
        onPress={() => navigation.popToTop()}
      />
    </View>
  );
}

Попробуйте написать на Snack

3.3 Резюме

  • navigation.navigate('RouteName') Если нового маршрута нет в стеке, он будет помещен в навигатор стека, в противном случае он перейдет на эту страницу.
  • navigation.push('RouteName') может несколько раз переходить на одну и ту же страницу маршрутизации.
  • Панель заголовка будет иметь кнопку «Назад» для обеспечения операций возврата, но мы можем использовать navigation.goBack() для выполнения операций возврата. Назад операция аппаратной кнопки «Назад» на Android имеет тот же эффект.
  • Вы можете использовать navigation.navigate('RouteName'), чтобы вернуться на существующую страницу, и вы можете использовать navigation.popToTop(), чтобы вернуться на первую страницу.
  • Свойство навигации доступно для всех компонентов страницы (при условии, что компонент определен как конфигурация маршрута и использует React Navigation для отображения маршрутов).

4 Передача параметров между маршрутами

Помните, я сказал: «Подробнее об этом позже, когда мы будем обсуждать параметры!»? Теперь пора начинать.

Теперь, когда мы знаем, как создать часть навигатора на маршрутизации стека и прыгать между маршрутами, как пройти параметры между маршрутизацией, давайте посмотрим.

Здесь есть две части:

  1. Поместите необходимые параметры маршрута в объект в качестве второго параметра функции navigation.navigate:navigation.navigate('RouteName', { /* params go here */ })
  2. Получите этот параметр в компоненте:route.params.

Мы рекомендуем передавать параметры в формате JSON. Таким образом, вы можете использоватьпостоянство состояния, и ваши экранные компоненты могут быть реализованы с использованием правильных соглашенийглубокая ссылка.

function HomeScreen({ navigation }) {
  return (
    <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
      <Text>Home Screen</Text>
      <Button
        title="Go to Details"
        onPress={() => {
          /* 1. Navigate to the Details route with params */
          navigation.navigate('Details', {
            itemId: 86,
            otherParam: 'anything you want here',
          });
        }}
      />
    </View>
  );
}

function DetailsScreen({ route, navigation }) {
  /* 2. Get the param */
  const { itemId } = route.params;
  const { otherParam } = route.params;
  return (
    <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
      <Text>Details Screen</Text>
      <Text>itemId: {JSON.stringify(itemId)}</Text>
      <Text>otherParam: {JSON.stringify(otherParam)}</Text>
      <Button
        title="Go to Details... again"
        onPress={() =>
          navigation.push('Details', {
            itemId: Math.floor(Math.random() * 100),
          })
        }
      />
      <Button title="Go to Home" onPress={() => navigation.navigate('Home')} />
      <Button title="Go back" onPress={() => navigation.goBack()} />
    </View>
  );
}

Попробуйте написать на Snack

4.1 Параметры обновления

Параметры также могут быть обновлены на странице, аналогично обновлению состояния страницы. navigation.setParams можно использовать для обновления параметров страницы. пройти черезAPI reference forпонять больше.

Вы также можете передать некоторые начальные параметры на страницу. Этот начальный параметр будет использоваться при переходе на страницу без установки каких-либо параметров. Они неглубоко объединены с переданными аргументами. Начальные параметры задаются в качестве свойства initialParams:

<Stack.Screen
  name="Details"
  component={DetailsScreen}
  initialParams={{ itemId: 42 }}
/>

4.2 Передача параметров на предыдущую страницу

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

Для этого вы можете использовать метод навигации, а если страница существует, вы можете использовать такой метод, как goBack. Вы можете передать параметры обратно через навигацию с параметрами:

function HomeScreen({ navigation, route }) {
  React.useEffect(() => {
    if (route.params?.post) {
      // Post updated, do something with `route.params.post`
      // For example, send the post to the server
    }
  }, [route.params]);

  return (
    <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
      <Button
        title="Create post"
        onPress={() => navigation.navigate('CreatePost', {
            text: route.params?.post ? route.params?.post : '',
        })}
      />
      <Text style={{ margin: 10 }}>Post: {route.params?.post}</Text>
    </View>
  );
}

function CreatePostScreen({ navigation, route }) {
  //将已经输入的内容,传递过来赋值给TextInput的处理逻辑
  let {text} = route.params;
  let [postText, setPostText] = React.useState(text);

  return (
    <>
      <TextInput
        multiline
        placeholder="What's on your mind?"
        style={{ height: 200, padding: 10, backgroundColor: 'white' }}
        value={postText}
        onChangeText={setPostText}
      />
      <Button
        title="Done"
        onPress={() => {
          // Pass params back to home screen
          navigation.navigate('Home', { post: postText });
        }}
      />
    </>
  );
}

Попробуйте написать на Snack

При нажатии кнопки «Готово» содержимое, введенное TextInput, будет возвращено на главную страницу и обновлено для отображения на странице.

4.3 Параметры передачи вложенной страницы навигации

Если у вас есть вложенный навигатор, параметры передачи немного отличаются. Например, у вас есть страница под названием «Учетная запись», и вы хотите передать параметры на страницу «Настройки». Необходимо сделать следующее:

navigation.navigate('Account', {
  screen: 'Settings',
  params: { user: 'jane' },
});

Узнайте больше, пожалуйста, нажмитеNesting navigators

4.4 Резюме

  • navigation и push могут передавать параметры как необязательный второй параметр при навигации по странице. Например:navigation.navigate('RouteName', {paramName: 'value'}).
  • на странице черезroute.paramsЧтение переданных параметров.
  • в состоянии пройтиnavigation.setParamsОбновите параметры страницы.
  • пройти черезinitialParamsСвойствам можно передавать параметры инициализации.

5 Настройте панель заголовка

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

5.1 Установите заголовок заголовка

Компонент принимает свойство options, которое является объектом или функцией, возвращающей объект, содержащий различные параметры конфигурации. Одним из них является заголовок, см. следующий пример:

function StackScreen() {
  return (
    <Stack.Navigator>
      <Stack.Screen
        name="Home"
        component={HomeScreen}
        options={{ title: 'My home' }}
      />
    </Stack.Navigator>
  );
}

Попробуйте "заголовок заголовка" на Snack

5.2 Использование параметров в заголовке

Чтобы использовать параметр в заголовке, нам нужно задать для страницы функцию, которая возвращает объект конфигурации. Полезно попробовать this.props в параметрах, но до тех пор, пока компонент не отобразится, нет экземпляра компонента, на который ссылаются, поэтому нет доступных реквизитов. Вместо этого, если мы установим параметры для функции, React Navigation вызовет ее с объектом, содержащим {Navigation, route} — в этом случае все, о чем мы заботимся, — это маршрут, который совпадает с передачей на страницу в качестве реквизита маршрута. Объектом реквизита является тот же самый объект. Помните, что мы можем получить параметры через route. параметр, ниже мы используем это, чтобы извлечь параметр и использовать его в качестве заголовка.

function StackScreen() {
  return (
    <Stack.Navigator>
      <Stack.Screen
        name="Home"
        component={HomeScreen}
        options={{ title: 'My home' }}
      />
      <Stack.Screen
        name="Profile"
        component={ProfileScreen}
        options={({ route }) => ({ title: route.params.name })}
      />
    </Stack.Navigator>
  );
}

Попробуйте «параметры в заголовке» на Snack

Аргумент, переданный в функцию параметров, является объектом со следующими свойствами:

В приведенном выше примере нам нужен только атрибут маршрута, а в некоторых случаях нам также нужен атрибут навигации.

5.3 Обновление опций с помощью setOptions

Обычно необходимо обновить конфигурацию параметров активной страницы в самом установленном компоненте экрана. Для этого мы можем использовать navigation.setOptions.

/* Inside of render() of React class */
<Button
  title="Update the title"
  onPress={() => navigation.setOptions({ title: 'Updated!' })}
/>

Попробуйте «обновить параметры навигации» на Snack.

5.4 Настройка стиля заголовка

Пользовательские стили заголовков имеют 3 ключевых свойства:headerStyle,headerTintColor,а такжеheaderTitleStyle.

  • headerStyle: объект стиля, применяемый к представлению, обертывающему заголовок. Если вы установите его backgroundColor, вы установите цвет заголовка.
  • headerTintColor: и кнопка «Назад», и заголовок используют это свойство в качестве цвета шрифта. В приведенном ниже примере мы устанавливаем оттенок белого ( #fff ), чтобы кнопка «Назад» и заголовок заголовка были белыми.
  • headerTitleStyle: вы можете использовать это для настройки fontFamily, fontWeight и других стилей текста.
function NavigationComponent() {
  return (
    <Stack.Navigator>
      <Stack.Screen
        name="Home"
        component={HomeScreen}
        options={{
          title: 'My home',
          headerStyle: {
            backgroundColor: '#f4511e',
          },
          headerTintColor: '#fff',
          headerTitleStyle: {
            fontWeight: 'bold',
          },
        }}
      />
    </Stack.Navigator>
  );
}

Попробуйте «стили заголовков» на Snack

требует внимания:

  1. В iOS шрифты и значки в строке состояния черные, не очень подходят для темного фона, мы не будем здесь это обсуждать, но вы должны обязательно настроить строку состояния так, чтобы она подходилаРуководство по строке состоянияваш цвет экрана.
  2. Мы настраиваем их только на главной странице. При переходе на страницу сведений заголовок возвращается к своему первоначальному стилю. Далее давайте посмотрим, как обмениваться стилями между страницами.

5.5 Параметры обмена между страницами

Обычно мы устанавливаем заголовок с одинаковым стилем на нескольких страницах. Например, цвет фирменного стиля вашей компании красный, а заголовок страницы должен иметь красный фон и белый шрифт. В приведенном выше примере мы настроили цвет шапки главной страницы, при переходе на страницу сведений заголовок возвращается к стилю по умолчанию. Если мы скопируем конфигурацию стиля главной страницы на страницу сведений, это будет очень хлопотно, а если этот стиль нужен каждой странице приложения? Вместо этого мы можем переместить конфигурацию стиля в свойство screenOptions в Stack.Navigator.

function StackScreen() {
  return (
    <Stack.Navigator
      screenOptions={{
        headerStyle: {
          backgroundColor: '#f4511e',
        },
        headerTintColor: '#fff',
        headerTitleStyle: {
          fontWeight: 'bold',
        },
      }}
    >
      <Stack.Screen
        name="Home"
        component={HomeScreen}
        options={{ title: 'My home' }}
      />
    </Stack.Navigator>
  );
}

Попробуйте «поделиться стилями заголовков» на Snack

Теперь страницы, принадлежащие StackScreen, будут иметь стиль темы, но если нам нужно переопределить эти конфигурации, как это сделать?

5.6 Замена заголовков пользовательскими компонентами

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

function LogoTitle() {
  return (
    <Image
      style={{ width: 50, height: 50 }}
      source={require('@expo/snack-static/react-native-logo.png')}
    />
  );
}

function StackScreen() {
  return (
    <Stack.Navigator>
      <Stack.Screen
        name="Home"
        component={HomeScreen}
        options={{ headerTitle: props => <LogoTitle {...props} /> }}
      />
    </Stack.Navigator>
  );
}

Попробуйте «настраиваемый компонент заголовка заголовка» на Snack

Вы могли заметить, почему мы устанавливаем headerTitle для компонента, а не для заголовка, как раньше? Поскольку headerTitle является свойством Stack.Navigator, headerTitle по умолчанию является текстовым компонентом, который используется для отображения заголовка.

5.7 Дополнительная конфигурация

ты сможешьcreateStackNavigator referenceПросмотрите список всех конфигураций в навигаторе стека.

5.8 Резюме

  • Вы можете настроить стиль заголовка страницы через свойства в настройках. прочитать все вариантыin the API reference.
  • options может быть объектом или функцией. Когда это функция, она предоставляет объект, содержащий свойства навигации и маршрута.
  • Во время инициализации общий стиль можно настроить с помощью screenOptions. Это свойство имеет приоритет над конфигурацией.

6 Header buttons

Теперь, когда мы знаем, как определить внешний вид заголовка, следующая операция может дать нам дополнительную мотивацию: реагировать на прикосновение пользователя через пользовательскую конфигурацию.

6.1 Добавить кнопку в шапку

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

function StackScreen() {
  return (
    <Stack.Navigator>
      <Stack.Screen
        name="Home"
        component={HomeScreen}
        options={{
          headerTitle: props => <LogoTitle {...props} />,
          headerRight: () => (
            <Button
              onPress={() => alert('This is a button!')}
              title="Info"
              color="#fff"
            />
          ),
        }}
      />
    </Stack.Navigator>
  );
}

Попробуйте «кнопку заголовка» на Snack

Когда мы определяем кнопку с помощью этого метода, поскольку это не переменная опция экземпляра HomeScreen, мы не можем использовать setState или другие методы экземпляра для ее изменения. Это очень важно, потому что очень часто в заголовки добавляют кнопки взаимодействия. Итак, давайте посмотрим, что делать дальше.

6.2 Взаимодействие между компонентами заголовка и страницы

Чтобы иметь возможность взаимодействовать с компонентами страницы, мы используем navigation.setOptions вместо свойства options для определения кнопки. Используя navigation.setOptions в компоненте страницы, мы можем получить доступ к параметрам страницы, состоянию, контексту и т. д.

function StackScreen() {
  return (
    <Stack.Navigator>
      <Stack.Screen
        name="Home"
        component={HomeScreen}
        options={({ navigation, route }) => ({
          headerTitle: props => <LogoTitle {...props} />,
        })}
      />
    </Stack.Navigator>
  );
}

function HomeScreen({ navigation }) {
  const [count, setCount] = React.useState(0);

  React.useLayoutEffect(() => {
    navigation.setOptions({
      headerRight: () => (
        <Button title="Update count!" onPress={() => setCount((c) => c + 1)} />
      ),
    });
  }, [navigation, setCount]);

  return <Text>Count: {count}</Text>;
}

Попробуйте «взаимодействие с заголовком» на Snack

6.3 Настройка кнопки «Назад»

createStackNavigator предоставляет кнопку «Назад» по умолчанию для определенной платформы. В iOS она включает в себя кнопку и текст. В доступном пространстве текст отображает заголовок предыдущей страницы, в противном случае отображается только содержимое «Назад».

Вы можете изменить поведение меток с помощью headerBackTitle и headerTruncatedBackTitle (Более).

вы можете использоватьheaderBackImageНастройте изображение кнопки «Назад».

6.4 Переопределение кнопки «Назад»

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

Как правило, это то, что вы хотите. Но в некоторых случаях вы можете предпочесть настроить кнопку «Назад» вместо того, чтобы использовать параметры, упомянутые выше, и в этом случае вы можете установить параметр headerLeft для элемента React, который будет отображаться, как мы сделали с headerRight. Кроме того, опция headerLeft также принимает компонент React, который можно использовать, например, для переопределения поведения onPress кнопки «Назад». Для получения дополнительной информации см.api reference.

6.5 Резюме

  • Вы можете использовать свойства headerLeft и headerRight в параметрах, чтобы установить кнопки в заголовке.
  • Кнопку «Назад» можно полностью настроить с помощью headerLeft, но если вы просто хотите изменить заголовок и изображение, вы можете установить другие свойства --headerBackTitle, headerTruncatedBackTitle и headerBackImage.

7 Вложенная навигация

Вложенная навигация означает отображение другой навигации на странице в одной навигации, например:

Сначала проделайте предварительную работу, установите необходимую библиотеку компонентов, которую здесь нужно использовать@react-navigation/bottom-tabs:

  • npm
npm install @react-navigation/bottom-tabs
  • yarn
yarn add @react-navigation/bottom-tabs

Затем начнем наше кодирование:

/* 自定义NestingNavigators.js文件,在App.js中引入即可 */
import React from 'react';
import {Text, View, Button} from 'react-native';
import {NavigationContainer} from '@react-navigation/native';
import {createStackNavigator} from '@react-navigation/stack';
import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';

function Feed() {
  return (
    <View
      // eslint-disable-next-line react-native/no-inline-styles
      style={{
        flex: 1,
        backgroundColor: '#e3e',
        justifyContent: 'center',
        alignItems: 'center',
      }}>
      <Text>Feed Screen</Text>
      <Button
        title="Go to Profile"
        onPress={() => navigation.navigate('Profile')}
      />
    </View>
  );
}

function Messages() {
  return (
    <View
      // eslint-disable-next-line react-native/no-inline-styles
      style={{
        flex: 1,
        backgroundColor: '#b33',
        justifyContent: 'center',
        alignItems: 'center',
      }}>
      <Text>Messages Screen</Text>
    </View>
  );
}

const Tab = createBottomTabNavigator();

function Home() {
  return (
    <Tab.Navigator>
      <Tab.Screen name="Feed" component={Feed} />
      <Tab.Screen name="Messages" component={Messages} />
    </Tab.Navigator>
  );
}

function Profile() {
  return (
    <View
      // eslint-disable-next-line react-native/no-inline-styles
      style={{
        flex: 1,
        backgroundColor: '#a3e',
        justifyContent: 'center',
        alignItems: 'center',
      }}>
      <Text>Profile Screen</Text>
    </View>
  );
}

function Settings() {
  return (
    <View
      // eslint-disable-next-line react-native/no-inline-styles
      style={{
        flex: 1,
        backgroundColor: '#e3a',
        justifyContent: 'center',
        alignItems: 'center',
      }}>
      <Text>Settings Screen</Text>
    </View>
  );
}

const Stack = createStackNavigator();

function NestingNavigators() {
  return (
    <NavigationContainer>
      <Stack.Navigator>
        <Stack.Screen name="Home" component={Home} />
        <Stack.Screen name="Profile" component={Profile} />
        <Stack.Screen name="Settings" component={Settings} />
      </Stack.Navigator>
    </NavigationContainer>
  );
}

export default NestingNavigators;

(Примечание. Официальный код документа был опущен. Я сделал полный код здесь. Заинтересованная детская обувь также может реализовать его самостоятельно!)

В приведенном выше примере компонент Home содержит навигацию по вкладкам. Во всем компоненте приложения компонент Home также используется на домашней странице навигации по стеку. Поэтому навигация по вкладкам здесь вложена в навигацию по стеку:

* Stack.Navigator
   。Home (Tab.Navigator)
      . Feed (Screen)
      . Messages (Screen)
   。Profile (Screen)
   。Settings (Screen)

Вложенные навигаторы работают так же, как вложенные обычные компоненты. Чтобы добиться желаемого поведения, часто необходимо вложить несколько навигаторов.

7.1 Каково влияние вложенной навигации

При вложении навигации следует помнить о нескольких вещах:

7.1.1 Каждый навигатор сохраняет свою историю навигации

Например, нажатие кнопки «Назад» во вложенном стеке навигации приведет к возврату на предыдущую страницу во вложенном стеке, даже если в качестве родительского представления используется другой навигатор.

7.1.2 Операция навигации обрабатывается текущим навигатором или его родительской навигацией, если она не может быть обработана.

Например, если вы вызываете navigation.goBack() на вложенной странице, она вернется к родительской навигации только в том случае, если вы уже находитесь на первой странице. Другие операции, такие как навигация, имеют тот же эффект, например, управление навигацией во вложенной навигации, но эта операция не обрабатывается, тогда родительская навигация будет обрабатывать операцию. В приведенном выше примере, когда вы вызываете navigation('Messages') на странице канала, навигация по вложенной вкладке будет обрабатывать это, но если вы вызываете navigation('Settings') , это будет обрабатывать навигатор родительского стека.

7.1.3 Вложенная навигация не получает родительские события

Например, если у вас есть навигация по стеку, вложенная в навигацию по вкладкам, страницы навигации по стеку не будут получать события уведомления от навигации по родительской вкладке, такие как добавление прослушивателя navigation.addListener, когда (tabPress). Чтобы получать родительские события навигации, вы можете использовать navigation.dangerousGetParent().addListener для прослушивания родительских событий.

7.1.4 Отображение пользовательского интерфейса родительской навигации поверх дочерней навигации

Например, при вложении стековой навигации в сворачиваемую (выдвижную) навигацию вы увидите свернутую страницу в верхней части стековой навигации. Однако если вы вложите свернутую навигацию в стековую навигацию, свернутая страница появится под заголовком. Это важный момент для того, как вы решаете, как вложить навигацию.

В своем приложении вы можете использовать эти шаблоны в зависимости от желаемого поведения:

  • Навигация стека вложена в каждую страницу свернутой навигации — свернутая страница отображается над заголовком стека.
  • Навигация по вкладкам вложена в начальную страницу навигации по стеку — при нажатии страницы новая страница перезапишет панель вкладок.
  • Навигация по стеку вложена в каждую страницу навигации по вкладкам — панель вкладок всегда видна. При нажатии на вкладку снова появляется вверху стека.

7.2 Переход на страницу во вложенной навигации

Рассмотрим следующий пример:

function Root() {
  return (
    <Stack.Navigator>
      <Stack.Screen name="Profile" component={Profile} />
      <Stack.Screen name="Settings" component={Settings} />
    </Stack.Navigator>
  );
}

const Drawer = createDrawerNavigator();

function NestingNavigators() {
  return (
    <NavigationContainer>
      <Drawer.Navigator>
        <Drawer.Screen name="Home" component={Home} />
        <Drawer.Screen name="Root" component={Root} />
      </Drawer.Navigator>
    </NavigationContainer>
  );
}

Здесь вы можете перейти с домашней страницы на страницу корневого стека:

navigation.navigate('Root');

Это нормально, будет отображаться начальная страница в корневом компоненте, да, Профиль. Но иногда вам может понадобиться управлять страницей, которую вы хотите отображать в навигации. Вы можете указать имя страницы в параметре, выполнив следующие действия:

navigation.navigate('Root', { screen: 'Settings' });

Теперь в навигации вместо Профиля будут отображаться Настройки.

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

7.2.1 Передача параметров на страницы во вложенной навигации

Вы также можете передавать параметры с указанным ключом параметра:

navigation.navigate('Root', {
  screen: 'Settings',
  params: { user: 'jane' },
});

Попробуйте написать на Snack

Переход на другую страницу при использовании навигации по стеку приведет к переходу на новую страницу, если навигация уже отрисована.

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

navigation.navigate('Root', {
  screen: 'Settings',
  params: {
    screen: 'Sound',
    params: {
      screen: 'Media',
    },
  },
});

В приведенном выше случае вы хотите перейти на страницу «Мультимедиа», которая находится внутри страницы «Звук», которая находится внутри страницы «Настройки».

7.2.2 Отображение заданного начального маршрута в навигаторе

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

Если вы хотите отображать указанную начальную страницу в навигации, вы можете отключить поведение использования указанной страницы в качестве начальной, установив initial: false:

navigation.navigate('Root', {
  screen: 'Settings',
  initial: false,
});

7.3 Лучшие практики при вложении

Мы рекомендуем свести вложенную навигацию к минимуму. Постарайтесь добиться желаемого поведения с минимальным количеством вложений. Вложенность имеет много недостатков:

  • Код становится трудным для понимания при переходе на вложенные страницы
  • Это приводит к глубоко вложенным иерархиям представлений, которые вызывают проблемы с памятью и производительностью на слабых устройствах.
  • Вложенные навигаторы одного типа (например, вкладка внутри вкладки, ящик внутри ящика и т. д.) могут запутать пользователя.

Думайте о вложенных навигаторах как о способе достижения желаемого пользовательского интерфейса, а не о способе организации вашего кода. Если вы хотите создать отдельные группы страниц для своей организации, храните их в отдельных объектах/массивах, а не в отдельных навигаторах.

8 Жизненный цикл навигации

В предыдущем разделе я манипулировал стековой навигацией с двумя страницами Home и Details и использовал navigation.navigate('RouteName') для перехода между маршрутами.

Важный вопрос в этой связи: что происходит, когда мы покидаем домашнюю страницу или возвращаемся на домашнюю страницу? Как пользователь узнает, что нужно уйти или вернуться на страницу маршрутизации?

Если вы посмотрите на реагирующую навигацию с точки зрения веба, то можете предположить, что когда пользователь переходит от маршрута А к маршруту Б, А будет размонтирован (будет вызываться компонент componentWillUnmount), а когда вернется к А, он будет снова загружен. . Хотя эти методы жизненного цикла React по-прежнему действительны и используются в реактивной навигации, их использование отличается от использования в Интернете. Это обусловлено более сложными потребностями мобильной навигации.

8.1 Пример сценария

Существует навигация стека с страницами A и B. После навигации к странице A вызывается его функция ComponentDIDMount. При нажатии на страницу B его компонент также называется, но A все еще установлен на стеке, поэтому его компонентWillunmount не вызывается.

При возврате к A со страницы B вызывается componentWillUnmount страницы B, но componentDidMount страницы A не вызывается, потому что A находился в стеке в течение всего жизненного цикла.

Аналогичные результаты можно наблюдать и с другими навигаторами (в комбинации). Рассмотрим навигацию по вкладкам с двумя вкладками, каждая из которых находится в стеке навигации:

/* Navigation生命周期 */
import React from 'react';
import {Text, View, Button} from 'react-native';
import {NavigationContainer} from '@react-navigation/native';
import {createStackNavigator} from '@react-navigation/stack';
import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';
/*这里就省略了,减少篇幅,具体代码可以在前面代码里找*/
function Profile() {...}
function Settings() {...}
function HomeScreen() {...}
function DetailScreen() {...}

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

function NavigationLifeCycle() {
  return (
    <NavigationContainer>
      <Tab.Navigator>
        <Tab.Screen name="First">
          {() => (
            <SettingsStack.Navigator>
              <SettingsStack.Screen name="Settings" component={Settings} />
              <SettingsStack.Screen name="Profile" component={Profile} />
            </SettingsStack.Navigator>
          )}
        </Tab.Screen>
        <Tab.Screen name="Second">
          {() => (
            <HomeStack.Navigator>
              <HomeStack.Screen name="Home" component={HomeScreen} />
              <HomeStack.Screen name="Details" component={DetailScreen} />
            </HomeStack.Navigator>
          )}
        </Tab.Screen>
      </Tab.Navigator>
    </NavigationContainer>
  );
}

export default NavigationLifeCycle;

Попробуйте написать на Snack

Просто начните с HomeScreen, затем перейдите к DetailsScreen. Затем переключите вкладку, чтобы отобразить страницу SettingsScreen, затем перейдите к ProfileScreen. Эта часть операции завершена, загружено 4 страницы! Если вы вернетесь к HomeStack, вы увидите страницу DetailsScreen — HomeStack сохранил состояние навигации.

8.2 События жизненного цикла React Navigation

Теперь, когда мы увидели, как методы жизненного цикла React работают в React Navigation, давайте ответим на вопрос, с которого мы начали: «Как пользователь узнает, что нужно размыть или сфокусироваться на перенаправленной странице?»

React Navigation выдал событие компоненту страницы события подписки. Мы можем прослушивать события Focus и BLUR, вы можете знать, входите ли вы на текущую страницу или покидаете текущую страницу.

Например:

function Profile({navigation}) {
  React.useEffect(() => {
    const unsubscribe = navigation.addListener('focus', () => {
      //Screen was focused
      //Do sometings
      console.log('Profile page is focused!!!');
    });
    return unsubscribe;
  }, [navigation]);
  return (
    <View
      // eslint-disable-next-line react-native/no-inline-styles
      style={{
        flex: 1,
        backgroundColor: '#a3e',
        justifyContent: 'center',
        alignItems: 'center',
      }}>
      <Text>Profile Screen</Text>
    </View>
  );
}

Попробуйте написать на Snack

видетьNavigation eventsУзнайте больше о событиях и использовании API

Вместо того, чтобы вручную добавлять прослушиватели событий, мы можем использовать для этого useFocusEffect. Он похож на useEffect в React, но тесно связан с жизненным циклом навигации.

Например:

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

function Profile({navigation}) {
  useFocusEffect(
    React.useCallback(() => {
      //Do something when the screen is focused
      console.log('Profile page is focused!!!');
      return () => {
        //Do something when the screen is unfocused
        // Useful for cleanup functions
        console.log('Profile page is unfocused!!!');
      };
    }, []),
  );
  return (
    <View
      // eslint-disable-next-line react-native/no-inline-styles
      style={{
        flex: 1,
        backgroundColor: '#a3e',
        justifyContent: 'center',
        alignItems: 'center',
      }}>
      <Text>Profile Screen</Text>
    </View>
  );
}

Попробуйте написать на Snack

Если вы хотите сделать страницу, если фокус основан на разных вещах, вы можете использовать UsiSfocused, он возвращает логическое значение, которое указывает, ориентирована ли страница.

8.3 Резюме

  • Хотя методы жизненного цикла React все еще работают, React Navigation добавляет больше событий, на которые можно подписаться через свойство навигации.
  • Вместо этого вы также можете использовать useFocusEffect или useIsFocused.

9 Открыть полноэкранный режим

Modal отображает временное содержимое, взаимодействующее с основным видом.

Мод блока бомбы похож - не является частью вашего основного навигационного процесса - который обычно имеет другой переход, другой подход уводит его и сосредоточиться на конкретном содержании или взаимодействии.

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

9.1 Создание модального стека

/* Navigation生命周期 */
import React from 'react';
import {Text, View, Button} from 'react-native';
import {NavigationContainer} from '@react-navigation/native';
import {createStackNavigator} from '@react-navigation/stack';

function HomeScreen({navigation}) {
  return (
    // eslint-disable-next-line react-native/no-inline-styles
    <View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
      <Text style={{fontSize: 30}}>This is the home screen!</Text>
      <Button
        title="Open Modal"
        onPress={() =>
          /* 1.传递参数到详情页面 */
          navigation.navigate('MyModal')
        }
      />
    </View>
  );
}

function DetailScreen() {
  return (
    // eslint-disable-next-line react-native/no-inline-styles
    <View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
      <Text>Detail Screen</Text>
    </View>
  );
}

function ModalScreen({navigation}) {
  return (
    // eslint-disable-next-line react-native/no-inline-styles
    <View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
      <Text style={{fontSize: 30}}>This is a Modal!</Text>
      <Button title="Dismiss" onPress={() => navigation.goBack()} />
    </View>
  );
}

const MainStack = createStackNavigator();
const RootStack = createStackNavigator();

function MainStackScreen() {
  return (
    <MainStack.Navigator>
      <MainStack.Screen name="Home" component={HomeScreen} />
      <MainStack.Screen name="Details" component={DetailScreen} />
    </MainStack.Navigator>
  );
}

function NavigationModal() {
  return (
    <NavigationContainer>
      <RootStack.Navigator mode="modal">
        <RootStack.Screen
          name="Main"
          component={MainStackScreen}
          options={{headerShown: false}}
        />
        <RootStack.Screen
          name="MyModal"
          component={ModalScreen}
          options={{headerShown: false}}
        />
      </RootStack.Navigator>
    </NavigationContainer>
  );
}

export default NavigationModal;

(注:官方文档里提供的代码有点问题,我这边完善了一下)

Попробуйте написать на Snack

Некоторые важные вещи, которые следует отметить:

  • Мы используем компонент MainStackScreen как страницу RootStackScreen! Здесь мы вкладываем стековую навигацию в другую стековую навигацию. Это совершенно верно, потому что мы хотим реализовать различные переходы, используя модальные окна. Поскольку RootStackScreen отображает навигатор стека и имеет собственный заголовок, мы также хотим скрыть заголовок для этой страницы. В будущем будет важно, чтобы, например, для навигации по вкладкам каждая вкладка имела свой собственный стек. Интуитивно вы ожидаете именно этого: когда вы переключаетесь с вкладки A на вкладку B, вы хотите, чтобы вкладка A сохраняла свое состояние навигации, продолжая перемещаться по вкладке B. Взгляните на эту диаграмму, в этом примере вы можете увидеть структуру навигации:

    image.png

  • При навигации по стеку свойство режима может быть: карточным (по умолчанию) и модальным. В iOS модальное поведение проводит пальцем по экрану снизу и позволяет пользователю провести пальцем сверху вниз, чтобы закрыть его. Модальное свойство не влияет на Android, так как полноэкранное модальное окно не имеет никакого другого поведения при переходе на платформе.

  • Когда мы вызываем навигацию, нам не нужно указывать ничего, кроме маршрута, к которому мы хотим перейти. Нет необходимости уточнять, к какому стеку он принадлежит (произвольно названному «корневому» или «основному» стеку) — React Navigator пытается найти маршрут в ближайшем навигаторе, а затем выполняет действие там. Чтобы визуализировать это, снова взгляните на древовидную диаграмму выше и представьте, что действие навигации происходит от главного экрана к основному стеку. Мы знаем, что MainStack не может обрабатывать маршрут MyModal, поэтому он передает его в RootStack, который может обрабатывать этот маршрут, что он и делает.

9.2 Сводка

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

  • navigation.navigate просматривает дерево навигаторов, чтобы найти навигатор, который может обрабатывать операции навигации.

10 технических терминов

Это новая часть документации, и в ней отсутствует много терминологии! Пожалуйста, отправьте запрос на вытягивание или проблему с терминологией, которую, по вашему мнению, следует объяснить здесь.

10.1 Header

Также известен как заголовок навигации, панель навигации, панель навигации и, возможно, многие другие. Это прямоугольник в верхней части экрана, содержащий кнопку «Назад» и заголовок экрана. Весь прямоугольник часто называют заголовком в React Navigation.

10.2 Navigator

Навигатор содержит элемент Screen в качестве дочернего элемента для определения конфигурации маршрута. NavigationContainer — это компонент, который управляет деревом навигации и содержит состояние навигации. Компонент должен обернуть все структуры навигатора. Обычно мы визуализируем этот компонент в корне нашего приложения, обычно это компонент, экспортированный из App.js.

function App() {
  return (
    <NavigationContainer>
      <Stack.Navigator> // <---- This is a Navigator
        <Stack.Screen name="Home" component={HomeScreen} />
      </Stack.Navigator>
    </NavigationContainer>
  );
}

10.2 Screen component

Компонент экрана — это тот, который мы используем в конфигурации маршрутизации.

const Stack = createStackNavigator();

const StackNavigator = (
  <Stack.Navigator>
    <Stack.Screen
      name="Home"
      component={HomeScreen} // <----
    />
    <Stack.Screen
      name="Details"
      component={DetailsScreen} // <----
    />
  </Stack.Navigator>
);

Суффикс Screen в именах компонентов совершенно необязателен, но является часто используемым соглашением. Мы можем назвать его Майклом, и он работает так же.

Ранее мы видели, что наш экранный компонент снабжен навигационной опорой. Важно отметить, что это происходит только тогда, когда экран отображается как маршрут с помощью React Navigation (например, в ответ на navigation.navigation). Например, если мы визуализируем DetailsScreen как дочерний элемент HomeScreen, DetailsProperty не будет поставляться с навигационной опорой, и когда вы нажмете кнопку «Перейти к деталям ...» на главном экране, приложение выдаст исключение «Типичное JavaScript». undefined не является объектом».

function HomeScreen() {
  return (
    <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
      <Text>Home Screen</Text>
      <Button
        title="Go to Details"
        onPress={() => navigation.navigate('Details')}
      />
      <DetailsScreen />
    </View>
  );
}

"Navigation prop reference"В этом разделе это рассматривается более подробно, описываются обходные пути и предоставляется дополнительная информация о других свойствах, доступных в навигационной опоре.

10.3 Navigation Prop

Этот реквизит будет передан на все страницы и может использоваться для следующих целей:

  • диспетчер отправит действие маршрутизатору
  • Навигация, возврат и т. д. могут использоваться для удобного планирования операций

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

Для получения более подробной информации см."Navigation prop document".

"Route prop reference"В этом разделе это рассматривается более подробно, описываются обходные пути и предоставляется дополнительная информация о других свойствах, доступных в реквизитах маршрута.

10.4 Route prop

Этот реквизит будет передан на все страницы. Содержит информацию о текущем маршруте, т.е. параметры, ключ и имя.

10.5 Navigation State

Состояние навигатора обычно выглядит так:

{
  key: 'StackRouterRoot',
  index: 1,
  routes: [
    { key: 'A', name: 'Home' },
    { key: 'B', name: 'Profile' },
  ]
}

Для этого состояния навигации есть два маршрута (могут быть вкладки или карточки в стеке). Индекс указывает на активный маршрут, которым является «B».

10.6 Route

Каждый маршрут представляет собой состояние навигации, которое содержит ключ для его идентификации и «имя», указывающее тип маршрута. Он также может содержать произвольные параметры:

{
  key: 'B',
  name: 'Profile',
  params: { id: '123' }
}

11 Слой совместимости

Примечание. Прежде чем следовать этому руководству, убедитесь, что вы настроили React Navigation 5 в своем приложении, следуя руководству по началу работы.

React Navigation 5 имеет совершенно новый API, поэтому наш старый код, использующий React Navigation 4, больше не будет работать с этой версией. Если вы новичок в новом API, вы можете прочитать различия в руководстве по обновлению. Мы знаем, что это может потребовать много работы, поэтому мы создали уровень совместимости, чтобы упростить этот процесс.

Чтобы использовать этот уровень совместимости, вам необходимо установить@react-navigation/compat:

  • npm
npm install @react-navigation/native @react-navigation/compat @react-navigation/stack
  • yarn
yarn add @react-navigation/native @react-navigation/compat @react-navigation/stack

Затем внесите небольшие изменения в наш код:

//“-”代表删除,“+”代表添加
-import { createStackNavigator } from 'react-navigation-stack';
+import { createStackNavigator } from '@react-navigation/stack';
+import { createCompatNavigatorFactory } from '@react-navigation/compat';
-const RootStack = createStackNavigator(
+const RootStack = createCompatNavigatorFactory(createStackNavigator)(
  {
    Home: { screen: HomeScreen },
    Profile: { screen: ProfileScreen },
  },
  {
    initialRouteName: 'Profile',
  }
);

Если вы ранее импортировали react-navigation, вам нужно изменить его сейчас и импортировать @react-navigation/compat:

//“-”代表删除,“+”代表添加
-import { NavigationActions } from 'react-navigation'; 
+import { NavigationActions } from '@react-navigation/compat';

Библиотека экспортирует следующий API:

  • Actions: -> NavigationActions -> StackActions -> DrawerActions -> SwitchActions
  • HOCs -> withNavigation -> withNavigationFocus
  • Navigators -> createSwitchNavigator
  • Помощники по совместимости -> createCompatNavigatorFactory — навигатор с использованием API v5 и возвращение createXNavigator с использованием API v4. -> createCompatNavigationProp — использует объект навигации v5 с объектом маршрута и возвращает объект навигации v4.

11.1 С чем он справляется?

Уровень совместимости обрабатывает различные различия API между React Navigation 4 и 5:

  • Используйте API статической конфигурации версии 4 вместо API на основе компонентов.
  • Измените сигнатуру метода в объекте навигации, чтобы она соответствовала v4.
  • Добавлена ​​поддержка screenProps, удаленная в версии 5.
  • Экспортируйте создателей действий, таких как NavigationActions, StackActions, SwitchActions, с той же подписью, что и v4.

11.2 С чем он не справляется?

Из-за динамического API React Navigation 5 статический API версии 4 больше не имеет некоторых функций, поэтому уровень совместимости не может их обрабатывать:

  • Он не упаковывает свойства или параметры навигатора. По сути, это означает, что параметры, которые вы передаете навигатору, могут отличаться из-за критических изменений в навигаторе. См. документацию к навигатору для API параметров обновления.
  • Устаревшие прямые ссылки не поддерживаются путем определения пути в конфигурации маршрутизации. видетьdeep linking documentationдля получения более подробной информации о том, как сейчас обрабатывается диплинкинг.
  • Переход к навигатору работает по-другому, то есть мы не можем перейти к экрану в навигаторе, который не был визуализирован, и мы не можем объединить параметры для всех подэкранов. См. документацию по вложенным навигаторам для получения дополнительной информации о том, как переходить к экранам в других навигаторах.
  • Некоторые методы, выполняющие ряд действий, например метод сброса в старом стиле, больше не поддерживаются. Неподдерживаемые методы выдают ошибки при их использовании, а если мы используем TypeScript, это выдаст ошибку типа.
  • Он не экспортирует createAppContainer, поэтому вам нужно использовать API v5 для контейнера (NavigationContainer). Это также означает, что все функции, поддерживаемые контейнером, необходимо перенести в новый API.
  • Если вы используете расширенные API, такие как интеграция с Redux, настраиваемые маршрутизаторы и действия, они больше не поддерживаются, и вам необходимо удалить интеграцию с Redux.

Хотя мы сделали все возможное, чтобы уровень совместимости обрабатывал большинство различий, некоторые вещи могут быть упущены. Поэтому обязательно протестируйте перенесенный код.

Почему мы должны его использовать?

Использование уровня совместимости позволяет нам постепенно переносить наш код на новую версию. К сожалению, нам пришлось изменить некоторый код, чтобы заставить работать уровень совместимости (см. «Что он не обрабатывает»), но это все же позволяет большей части нашего кода оставаться прежним. Некоторые из преимуществ использования слоя совместимости включают в себя:

  • Это позволяет нам писать новый код с использованием нового API при интеграции с кодом с использованием устаревшего API, т. е. вы можете переходить от кода, написанного в новом API, к экранам, определенным в устаревшем API, и наоборот.
  • Поскольку он построен на версии 5 с отличной поддержкой TypeScript, старый код также может использовать преимущества улучшенной проверки типов, что может быть полезно, когда вы захотите позже преобразовать его в новый API.
  • Вы можете прочитать больше о миграции, например о переносе только нескольких методов в компоненте на новый API. У вас по-прежнему есть доступ к объекту навигации версии 5 в navigation.original, который можно использовать для постепенного переноса кода.
  • Вы можете получить доступ к новым API в старых компонентах, таких как navigation.setOptions, или новых хуках, таких как useFocusEffect.

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

12 Устранение неполадок

В этом разделе делается попытка описать проблемы, с которыми пользователи часто сталкиваются, когда впервые привыкают к React Navigation. Эти проблемы могут быть связаны или не связаны с самой React Navigation.

Перед устранением проблемы убедитесь, что вы обновили пакет до последней доступной версии. Вы можете установить последнюю версию, установив пакет еще раз (например, npm install package-name).

12.1 обновлен до последней версии, получая ошибку «Невозможно разрешить модуль»

Есть три причины:

Устаревший кеш для сборщика Metro

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

Если вы используете Expo, запустите:

expo start -c

Если вы не используете Expo, запустите:

npx react-native start --reset-cache

Если это не сработает, сделайте следующее:

rm -rf $TMPDIR/metro-bundler-cache-*

12.2 Отсутствующие одноранговые зависимости

Если модуль указывает на пакет npm (т. е. модуль имеет имя без ./), это может быть вызвано отсутствием одноранговых зависимостей. Чтобы это исправить, установите зависимости в свой проект:

  • npm
npm install name-of-the-module
  • yarn
yarn add name-of-the-module

Иногда это может быть даже вызвано неправильной установкой. Если очистка кеша не работает, попробуйте удалить папку node_modules и снова запустить npm install.

12.3 Отсутствующие расширения в конфигурации метро

Иногда об ошибке сообщается так:

Error: While trying to resolve module "@react-navigation/native" from file "/path/to/src/App.js", the package "/path/to/node_modules/@react-navigation/native/package.json" was successfully found. However, this package itself specifies a "main" module field that could not be resolved ("/path/to/node_modules/@react-navigation/native/src/index.tsx"

Это может произойти, если у вас есть пользовательская конфигурация для Metro и вы не указали ts и tsx в качестве допустимых расширений. Эти расширения присутствуют в конфигурации по умолчанию. Чтобы проверить наличие этой проблемы, найдите файл metro.config.js в своем проекте и проверьте, указана ли опция sourceExts. Он должен иметь как минимум следующую конфигурацию:

sourceExts: ['js', 'json', 'ts', 'tsx'];

Если эти расширения отсутствуют, добавьте их, а затем очистите кэш Metro, как показано в предыдущем разделе.

12.4 Ошибка «SyntaxError in @react-navigation/xxx/xxx.tsx» или «SyntaxError: /xxx/@react-navigation/xxx/xxx.tsx: неожиданный токен»

Это может произойти, если вы используете более старую версию пакета Metro-react-native-babel-preset. Самый простой способ исправить это — удалить node_modules, а также файл блокировки и переустановить зависимости.

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

rm -rf node_modules
rm package-lock.json
npm install

Если использовать пряжу:

rm -rf node_modules
rm yarn.lock
yarn

Вам также может понадобиться очистить кеш сборщика Metro, как описано ранее на этой странице.

12.5 Ошибка «Модуль '[...]' не имеет экспортированного члена 'xxx' при использовании TypeScript"

Это может произойти, если в вашем проекте установлена ​​более старая версия TypeScript. Вы можете попробовать обновить его:

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

npm install --save-dev typescript

Если использовать пряжу:

yarn add --dev typescript

12.6 Ошибка «null не является объектом (оценка «RNGestureHandlerModule.default.Direction»)»

Эта и некоторые подобные ошибки могут возникнуть, если вы не подключаете библиотеку react-native-gesture-handler.

Автоматическое связывание, начиная с React Native 0.60, поэтому, если вы связывали библиотеку вручную, сначала отсоедините ее:

react-native unlink react-native-gesture-handler

Если вы тестируете iOS и используете Mac, убедитесь, что вы запустили pod install в папке ios/:

cd ios
pod install
cd ..

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

12.7 После добавления вида на экране ничего не видно

При заключении контейнера в представление обязательно используйте flex: 1, который растягивает представление, чтобы заполнить контейнер.

import * as React from 'react';
import { View } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';

export default function App() {
  return (
    <View style={{ flex: 1 }}>
      <NavigationContainer>{/* ... */}</NavigationContainer>
    </View>
  );
}

12.8 Предупреждение: «В состоянии навигации были обнаружены несериализуемые значения»

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

Примеры распространенных вариантов использования для передачи функций в параметрах:

  • Передайте обратный вызов, который будет использоваться в кнопке заголовка. Вместо этого вы можете использовать navigation.setOptions. Для примера см.guide for header buttons.
  • Передайте обратный вызов следующему экрану, который можно вызвать для передачи некоторых данных обратно. Как правило, для этого можно использовать навигацию. Например, см. руководство вguide for params.
  • Передавайте сложные данные на другой экран. Вместо передачи параметра данных вы можете хранить эти сложные данные в другом месте (например, в глобальном хранилище) и передавать идентификатор. Затем экран может использовать идентификатор для извлечения данных из глобального хранилища.

Если вы не используете сохранение состояния или прямые ссылки на экраны, которые принимают формы параметров, предупреждение не повлияет на вас, и вы можете спокойно его игнорировать. Чтобы игнорировать предупреждения, можно использовать YellowBox.ignoreWarnings.

Например:

import { YellowBox } from 'react-native';

YellowBox.ignoreWarnings([
  'Non-serializable values were found in the navigation state',
]);

12.9 Приложение не работает должным образом при подключении к отладчику Chrome

Когда ваше приложение подключено к отладчику Chrome (или другим инструментам, использующим отладчик Chrome, например React Native Debugger), вы можете столкнуться с различными проблемами, связанными со временем.

这可能会导致诸如按钮按下需要花费很长时间才能注册或根本无法工作,手势和动画缓慢且有错误等问题。还可能存在其他功能问题,例如promises无法解决,超时和间隔无法正常工作等。 Слишком

Эти проблемы связаны не с React Navigation, а с тем, как работает отладчик Chrome. При подключении к отладчику Chrome все ваше приложение будет работать в Chrome и взаимодействовать с собственным приложением через сокеты в сети, что может вызвать задержки и проблемы, связанные со временем.

Поэтому, если вы не пытаетесь выполнить отладку, лучше протестировать приложение без подключенного отладчика Chrome. Если вы используете iOS, вы можете использовать Safari для отладки приложения, которое отлаживает приложение непосредственно на устройстве и не имеет этих проблем, хотя у него есть другие недостатки.

13 Ограничения

Как потенциальному пользователю библиотеки важно знать, что вы можете и что не можете с ней делать. Вооружившись этими знаниями, вы можете выбратьa different library instead. Мы будем pitch & anti-pitchОбсуждая проектные решения высокого уровня, здесь мы представим некоторые варианты использования, которые либо не поддерживаются, либо настолько сложны для реализации, что невозможны. Если какое-либо из следующих ограничений является критическим фактором для вашего приложения, React Navigation может вам не подойти.

13.1 Ограниченная поддержка раскладки справа налево (RTL)

Мы пытаемся правильно обрабатывать макет RTL в React Navigation, но команда, работающая над React Navigation, невелика, и в настоящее время у нас нет пропускной способности или процесса для тестирования всех изменений в макете RTL. Таким образом, у вас могут быть проблемы с макетом RTL.

Если вам нравится то, что может предложить React Navigation, но оно отключено из-за этого ограничения, мы рекомендуем вам принять участие и взять на себя поддержку макета RTL. Свяжитесь с нами в Твиттере:@reactnavigation.

13.2 Некоторые особенности поведения платформы

React Navigation не поддерживает функции просмотра и всплывающих окон, доступные на устройствах с поддержкой 3D Touch.

Следующая глава: Навигация RN Routing-React — навигация по вкладкам

Справочная документация:React Navigation - Fundamentals