В мире с множеством решений для маршрутизации для реактивных проектов, зачем мне создавать свои собственные? Почему я не мог просто использовать один из вариантов, обсуждаемых в этой теме на 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-летней карьеры офицера ВМС США. Ему нравится применять свои знания и опыт в программировании, системной инженерии и оперативном планировании для программирования.