Более простая относительная маршрутизация для реактивного маршрутизатора

И я имею в виду более простой, который включает в себя Links (NavLinks) и Switch.

Маршрутизатор React — отличный инструмент, позволяющий быстро и легко настроить маршруты приложений. Тем не менее, когда ваше приложение становится более сложным, требующим относительной маршрутизации (и связывания), требуется немного больше работы и осведомленности. Теперь есть несколько решений, в основном адаптирующих ваш путь к <Route path={`${match.url}/<desired-relative-path>`} component={...}/> и аналогично <Link to={`${match.url}/<desired-relative-url>`}>Go to xyz</Link> . Хотя они работают, они становятся громоздкими и требуют каждый раз делать match доступным с помощью вспомогательной функции withRouter.

Для тех, кто знаком с Angular (2 и выше), это решается довольно элегантно с помощью маршрутизации на уровне модулей, однако в react-router нет такого механизма из коробки.

Так что, если бы мы могли просто аннотировать любые Route , Link или NavLink как relative и волшебство сделало за нас? Что ж, теперь мы можем. Мы даже можем безопасно смешивать абсолютные и относительные сущности и не только это, помещать их в Switch, и они все равно будут работать правильно.

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

Во-первых, код магии:

А затем используйте его так:

Как это работает? Давайте начнем со схемы, чтобы добиться такого поведения, вам необходимо:
1. Адаптировать LinkNavLink) от относительного значения to к абсолютному.
2. Адаптировать Route от относительного path к абсолютному.
3. Адаптируйте Switch так, чтобы он определял относительные Route дочерние элементы, и адаптируйте их. Это требует специальной обработки, см. объяснение ниже.

Модификации 1+2, т.е. адаптация Link и Route должны выполняться только при включенном флаге relative, в противном случае исходный элемент достаточно хорош.

А теперь к конкретному разделу кода:

Строки 1–10:

Импортируйте необходимые зависимости и исходные (= базовые) компоненты.

Строки 11–18:

Определите служебную функцию для удаления реквизита, не требуемого подкомпонентами. Это делается для того, чтобы избежать жалоб на флаг statusContext (добавленный withRouter) или relative (добавленный пользователем), которые не используются фактическими базовыми компонентами (Route и Link).

Строки 19–20:

Служебные функции для обработки конкретного случая корня, который возвращается как "/", а не как "", и для преобразования url и path (= относительный путь) в абсолютный путь.

Строки 22–25:

Эта функция заботится о том, чтобы не изменять элементы, которые не помечены как relative, пропуская их, она делает это с помощью функции recompose branch(). Сначала проверяется флаг relative, если он равен true, применяется предоставленная функция для получения нового HOC, в противном случае используется исходный компонент.

Строки 27–28:

Вспомогательная функция для адаптации path свойств маршрутов.

Строки 30–36:

Более сложная служебная функция для адаптации свойства to для LinkNavLink). Эта функция должна обрабатывать 2 разные ситуации, где исходное значение to является строкой, а где это объект.

Строки 38–42:

Создайте измененный корень, используя compose и применив 3 преобразования HOC:
1. Используйте withRouter, чтобы убедиться, что измененный компонент имеет доступ к match и, следовательно, к текущему совпадающему url.
2. Изменить (только когда он является относительным) path к абсолютному.
3. Удален флаг (свойство) relative, который не является частью свойств Route's.

Строки 44–50:

Аналогично предыдущему разделу, но для Link и NavLink. Сначала служебное преобразование объединяется путем составления 3 преобразований HOC: добавление маршрута, изменение цели на абсолютную и удаление relative и staticContext. Затем он применяется к BaseLink и BaseNavLink для получения адаптированных HOC Link и NavLink.

Строка 52–69:

Адаптируйте Switch для работы с относительным Route. Эта часть является более сложной и требует небольшого понимания React и исходной реализации Switch.
Исходная реализация просматривает дочерние элементы и ищет те, которые имеют свойство path. Затем он ищет первого такого дочернего элемента, который соответствует текущему местоположению, и игнорирует остальные. Итак, давайте предположим, что мы используем переключатель на 2 рычага вниз, чтобы обернуть 2 относительных маршрута. Это означает, что текущий URL-адрес /level1/level2 и что в нашем дереве реакций отображается следующий код:

<Switch>
  <Route relative path="/more/summary" component={...}/>
  <Route relative path="/more/details" component={...}/>
</Switch>

Исходный код Switch не знает об относительном флаге и попытается сопоставить значение path с текущим URL-адресом, то есть проверить, соответствует ли /level1/level2/more/summary /more/summary (или /more/details). Очевидно, что это совпадение не удастся, и эти маршруты будут проигнорированы. Что делает код модификации, так это сопоставляет children, каждый дочерний элемент с флагом relative клонируется, на этот раз со свойством path, установленным на абсолютный путь (путем соединения известного url и относительного path) и без флага relative (поскольку он уже в абсолютном выражении). Дети, у которых нет флага relative, проходят без модификаций. Это сложный момент. Первоначально, поскольку относительные Routes адаптированы, мы думаем, что эта модификация не нужна. Но оказывается, логика исходного Switch считывает значение времени создания экземпляра свойств (child.props.path), которые модифицируются и адаптируются только позже. Таким образом, он всегда видит относительный путь, а не соответствующее значение абсолютного пути. Это исправляется переключением компонента на тот, который использует абсолютный путь.

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

Вывод

Реагировать круто и сложно. Вы можете продолжать писать шаблонный код снова и снова, но если вы углубитесь в принципы реагирования и использования HOC, вы сможете сделать свой код более элегантным, лаконичным и стабильным.
В этом посте я показал, как создавать ваш собственный код относительной маршрутизации, который заменяет необходимость написания подробного кода (используйте withRouter, получите match и url, а затем объедините их) простым единственным флагом: relative. Надеюсь, вы найдете ее полезной.