Раздражающая проблема дизайна с многоразовым решением.

Обновление. Мне стало известно, что этот (по общему признанию хакерский) подход перестал работать в новейших версиях React Navigation. Это всегда была временная задержка, поэтому я ожидал, что этот подход в конечном итоге потерпит неудачу. При этом Джайвант Мулик предложил решение, которое работает с React Navigation v1.1.2, поэтому, если вы используете эту или аналогичную версию, это может по-прежнему работать для вас.

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

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

Проблема

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

const ModalStack = StackNavigator({
  Home: { screen: MyHomeScreen },
  Profile: { screen: MyProfileScreen },
}, {
  mode: 'modal',
});

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

// Main Card-Style Navigator
const MainStack = StackNavigator({
  Home: { screen: HomeScreen },
  Detail: { screen: DetailScreen }
});
// Modal-Style Navigator
const ModalStack = StackNavigator({
  Home: { screen: MainStack },
  Modal: { screen: ModalScreen },
}, {
  mode: 'modal',
});

Достаточно просто, правда? Но… у нас проблема.

«Двойные заголовки! Весь путь! Что это значит?"

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

// Modal-Style Navigator
const ModalStack = StackNavigator({
  Home: { screen: MainStack },
  Modal: { screen: ModalScreen },
}, {
  mode: 'modal',
  headerMode: 'none',
});

Большой! У нас снова остался только один заголовок! Но… теперь у нас нет заголовка на модальном экране. Проклятие!

Решение

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

Вместо этого давайте возьмем этот компонент заголовка прямо из недр исходного кода React Navigation и повторно используем этот компонент отдельно на нашем модальном экране!

Мы можем найти нужный нам файл в react-navigation / src / views / Header / Header.

Заявление об ограничении ответственности: предложение извлечь это представление и повторно использовать его сначала было предложено в другом месте в Интернете. У меня возникают проблемы с отслеживанием исходной статьи / переполнения стека, в которой я нашел это предложение, но я обновлю статью, как только ее найдет я или кто-то другой.

import Header from 'react-navigation/src/views/Header/Header';
class ModalScreen extends React.Component {
  render() {
   return (
     ...
     <Header scene={{index: 0}}
             scenes={[{index: 0, isActive: true}]}
             navigation={{state: {index: 0}}}
             getScreenDetails={() => ({options: {
                title: 'Modal',
                headerRight: (
                  <Button 
                     title="Close" 
                     onPress={() => this.props.navigation.goBack()}
                  />
                )
             }})}
     />
     ...
   );
  }
}

Ладно ... это некрасиво. Но работа выполняется, и она постоянна и повторяема. Мы сообщаем React Navigation, что хотим выделить их компонент заголовка, а затем запутываем компонент, принудительно вводя в него базовые данные, сообщая компоненту, что мы находимся на первой сцене (индекс сцены = 0) и что он может просто игнорировать состояние навигации. Кроме того, мы сообщаем заголовку, что у нас есть куча фальшивых сцен с помощью строки scenes={[{index: 0, isActive: true}]} (спасибо Джайванту Мулику за указание на это). Затем мы передаем объект, очень похожий на тот, который мы использовали бы в статике. navigationOptions на нормальном экране и вуаля - у нас последовательный заголовок!

Забрать

Это хакерский метод - не идеальный, но он выполняет свою работу. Оберните это в компонент, который можно использовать повторно для всех ваших модальных окон, и назовите это днем, как я. Надеюсь, в будущем компонент заголовка будет представлен с более приятным API, но пока этого не произойдет, этот небольшой прием не так уж и плох!

Дальнейшее чтение

Полный пример в закуске: https://snack.expo.io/H1NijfdtM