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

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

Во-первых, я попытался интегрировать свою существующую нативную навигационную систему с Redux. Я думал, что буду отправлять действия для изменения элемента состояния в моем хранилище избыточности, которые затем сообщат реакции-навигации, куда идти. К сожалению, я не врубил редукционную интеграцию с реакт-навигацией; вероятно, из-за того, как я связал свой проект и редукс. Мои модальные окна были настоящими модальными компонентами, нативными для реакции, и не имели доступа к навигатору реакции-навигации.

Кроме того, я попробовал react-native-router-flux, который отображал очень медленные переходы страниц после того, как я его реализовал (это могло быть при запуске проекта через expo.io, я не уверен).

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

Но я не позволю этому остановить меня…

Основная концепция заключается в условной визуализации компонента на основе значения, хранящегося в моем хранилище избыточности. Выбранный компонент визуализируется App component. Это компонент самого высокого уровня в моем приложении, который действует как фрейм, содержащий различные экраны приложения (страницы, сцены, выберите любую метафору по вашему желанию), поскольку они меняются местами в ответ на действия пользователя. Мне также нужен стек, чтобы вставлять экраны (на самом деле просто строки имён экранов) по мере того, как я переходил к ним, чтобы обеспечить обратную навигацию. Я добавил следующий объект в свой магазин избыточности для хранения этой информации:

navigation:{
  currentScreen: "Home",
  screenStack: ["Home"]

…как видите, я не изобретателен в присвоении имен своим переменным.

Компонент App выглядит следующим образом:

<Provider store={store}>
  <View style={StyleSheet.absoluteFill}>
    {screens[this.props.appState.navigation.currentScreen].screen
  </View>
</Provider>

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

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

const screens = {
Home: { screen: <Home/>, default: true },
Story: { screen: <Story/> },About: { screen: <About/> },
...
}

Оглядываясь назад на App component, можно увидеть, что если currentScreen prop в моем хранилище редуктов равно “Home”, то {screens[this.props.appState.navigation.currentScreen].screen} возвращает Home component. Это приводит к тому, что Home component отображается внутри компонента App «frame».

Затем я создал избыточные действия для перемещения «вперед» к новым экранам или обратно к предыдущим экранам в стеке. Например:

this.props.dispatch(actions.appStateActions.navigateTo('About'));

отправляет следующее действие:

navigateTo: (screen)=>{
  return{
    type: 'NAVIGATE_TO',
    screen
  }
},

который, в свою очередь, вызывает этот редьюсер, который изменяет currentScreen variable в хранилище избыточности приложения на «О программе»:

case 'NAVIGATE_TO':
  return Object.assign({}, appState, { navigation:
    Object.assign({}, appState.navigation, {
      currentScreen: action.screen,
      screenStack:
        appState.navigation.screenStack.concat(action.screen)
    })
  });

Нет, в настоящее время я не использую immutable.js; да, я знаю, что я должен попробовать это некоторое время. Когда-нибудь я этим займусь.

Навигация назад работает аналогично, за исключением того, что редюсер выглядит так:

case 'NAVIGATE_BACK':
  if(appState.navigation.screenStack.length > 1){ // make sure there   is something in the stack to go back towards
  // get the screen to go back to
    let backTarget = appState.navigation.screenStack.slice(appState.navigation.screenStack.length - 2)[0]; // get the string at index 0 since I want the string, not the array the string is in
  // get the remaining screens so that they can become the updated
    stack array let correctArray = appState.navigation.screenStack.slice(0, - 1);
  
    return Object.assign({}, appState, { navigation: Object.assign({}, appState.navigation, { currentScreen: backTarget, screenStack: correctArray })
  });
}
//just return the current appState if there is nothing to go back to return appState;

Наконец, на устройствах Android есть кнопка Назад, при нажатии которой требуется переход на предыдущий экран. Я использовал BackAndroid, чтобы зафиксировать нажатие кнопки Назад и вызвать действие navigationBack(). Я создал функцию слушателя в методе componentWillMount() компонента App component.

BackAndroid.addEventListener('hardwareBackPress', function () {
  if (that.props.appState.navigation.currentScreen !== 'Home') {
    that.props.dispatch(actions.appStateActions.navigateBack());
    return true;
  }
  return false;
});

Обратный вызов слушателя должен возвращать true, если событие обрабатывается программой, и false, если нет. В этом случае программе нужно вернуться назад только в том случае, если пользователь не находится на Homeэкране. В противном случае обратный вызов возвращает false, указывая, что пользователь находится на экране Home и что Android должен выйти из программы.

Вот и все! Результатом всего этого является система, которая позволяет мне постоянно перемещаться в любом месте моего приложения.

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

Реджинальд Джонсон сохранял страсть к программированию на протяжении всей своей более чем 20-летней карьеры офицера ВМС США. Ему нравится применять свои знания и опыт в программировании, системной инженерии и оперативном планировании для программирования.