Другое

Руководство по обработке слэшей в конце URL в React Router v6+

Узнайте, как автоматически добавлять слэши в конце URL к языковым ссылкам в React Router v6+. Полное руководство с примерами кода для последовательной обработки URL и подсветки навигации.

Как автоматически добавлять косые черты в конце к ссылкам в React Router v6+?

У меня возникла проблема с React Router, когда мне нужно автоматически добавлять косую черту (/) в конце к языковым ссылкам, таким как {domain}/en. Однако React Router v6+ рассматривает /:lang и /:lang/ как один и тот же путь, что вызывает проблемы с моим компонентом Navigation.

Вот моя текущая реализация:

App.jsx:

jsx
import { Routes, Route, Navigate, useParams } from "react-router";
import Layout from "./components/Layout/Layout";
import Main from "./components/main/main";
import Undefined from "./components/Pages/NotFound";

export default function App() {
    return (
        <Routes>
            <Route path="/:lang?/" Component={Layout}>
                <Route index Component={Main} />
                <Route path="questions" Component={Main} />
            </Route>
            <Route path="*" Component={Undefined} />
        </Routes>
    );
}

Layout.jsx:

jsx
import { Outlet, useParams } from "react-router";
import Header from "../Header/Header";
import Footer from "../Footer/Footer";
import Navigation from "../navigation/navigation";
import Undefined from "../Pages/NotFound";
function Layout() {
    const suportedLanguages = ["en", "ru", "uk"];
    const lang = useParams().lang || "en";
    if (!suportedLanguages.includes(lang)) {
        return (<Undefined />);
    }
    console.log(lang);
    return (
        <div className="flex column">
            <Header />
            <div className="flex">
                <Navigation />
                <Outlet />
            </div>
            <Footer />
        </div>
    );
}

export default Layout;

Navigation.jsx:

jsx
import styles from "./navigation.module.css";

import CustomNavLink from "../CustomLinks/CustomNavLink";
function Navigation({lang}) {
    console.log(lang);
    return (
        <aside className={styles.asideContainer}>
            <CustomNavLink to={`/${lang}/`} end>
                Home
            </CustomNavLink>
            <CustomNavLink to={`/${lang}/tags`}>
                Tags
            </CustomNavLink>
            <CustomNavLink to={`/${lang}/questions`}>
                Questions
            </CustomNavLink>
        </aside>
    );
}

export default Navigation;

Компонент Navigation работает не так, как ожидалось, из-за того, как React Router обрабатывает путь с и без косой черты в конце. Как я могу гарантировать, что все языковые ссылки автоматически включают косую черту в конце?

Автоматическое добавление конечных слэшей в ссылках в React Router v6+

В React Router v6+ вы можете автоматически добавлять конечные слэши в ссылки с помощью свойства end в компонентах NavLink и правильной настройки ваших маршрутов для обработки конечных слэшей. Поскольку React Router v6+ рассматривает пути с и без конечного слэша как идентичные, вам потребуется реализовать решение, которое обеспечит последовательное поведение конечных слэшей в ваших ссылках.


Содержание


Понимание поведения конечных слэшей в React Router v6+

React Router v6+ принципиально изменил способ обработки конечных слэшей по сравнению с v5. Как документировано в руководстве по обновлению React Router, весь сопоставление путей в v6 игнорирует конечный слэш в URL, а свойство strict было удалено и не действует в v6 источник.

Это означает, что такие маршруты, как /:lang и /:lang/, рассматриваются как идентичные алгоритмом сопоставления React Router источник. Однако свойство end в компонентах NavLink теперь контролирует, должен ли URL точно заканчиваться в этой точке, что влияет на поведение конечного слэша источник.


Решение 1: Использование свойства end для точного соответствия

Наиболее прямое решение - использовать свойство end в ваших компонентах NavLink для обеспечения точного соответствия с конечным слэшем:

jsx
<CustomNavLink to={`/${lang}/`} end>
    Главная
</CustomNavLink>
<CustomNavLink to={`/${lang}/tags`}>
    Теги
</CustomNavLink>

При использовании end={true} (или просто end) NavLink будет активным только тогда, когда URL точно соответствует указанному пути, включая конечный слэш. Это гарантирует, что ваша навигация подсвечивает правильный маршрут, когда URL содержит конечный слэш источник.


Решение 2: Конфигурация маршрутов с конечными слэшами

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

jsx
<Routes>
    <Route path="/:lang?/" Component={Layout}>
        <Route index Component={Main} />
        <Route path="questions/" Component={Main} />
    </Route>
    <Route path="*" Component={Undefined} />
</Routes>

Этот подход гарантирует, что все ваши маршруты определены с конечными слэшами, создавая последовательность во всем приложении. Шаблон /:lang?/ делает языковой параметр необязательным, но при его наличии обеспечивает наличие конечного слэша источник.


Решение 3: Пользовательский компонент навигации с принудительным добавлением конечного слэша

Создайте улучшенный компонент Навигации, который автоматически добавляет конечные слэши:

jsx
import { NavLink, useParams } from "react-router";
import styles from "./navigation.module.css";

function Navigation() {
    const supportedLanguages = ["en", "ru", "uk"];
    const lang = useParams().lang || "en";
    
    if (!supportedLanguages.includes(lang)) {
        return null;
    }

    const navItems = [
        { path: `/${lang}/`, label: "Главная" },
        { path: `/${lang}/tags/`, label: "Теги" },
        { path: `/${lang}/questions/`, label: "Вопросы" }
    ];

    return (
        <aside className={styles.asideContainer}>
            {navItems.map((item) => (
                <NavLink 
                    key={item.path} 
                    to={item.path} 
                    end={item.path.endsWith('/')}
                    className={({ isActive}) => 
                        isActive ? styles.active : styles.inactive
                    }
                >
                    {item.label}
                </NavLink>
            ))}
        </aside>
    );
}

export default Navigation;

Эта реализация:

  • Автоматически добавляет конечные слэши ко всем путям навигации
  • Правильно использует свойство end для путей, заканчивающихся слэшами
  • Обеспечивает последовательную обработку активного состояния

Альтернативный подход: Обработка конечных слэшей на стороне сервера

Для более комплексного решения рассмотрите обработку конечных слэшей на уровне сервера. Как упоминалось в обсуждениях React Router, “также следует добавить примечание в документацию v6 о настройке сервера для добавления/удаления конечного слэша через перенаправление в целях SEO” источник.

Конфигурация на стороне сервера (пример для Nginx):

nginx
location / {
    if ($request_uri ~ ^/[^/]+[^/]$) {
        rewrite ^(.*)$ $1/ permanent;
    }
    try_files $uri $uri/ /index.html;
}

Это гарантирует, что все URL нормализуются с конечными слэшами до того, как достигнут ваше React-приложение.


Пример полной реализации

Вот полная реализация, которая решает вашу конкретную задачу маршрутизации по языкам:

jsx
// App.jsx
import { Routes, Route, Navigate } from "react-router-dom";
import Layout from "./components/Layout/Layout";
import Main from "./components/main/main";
import Undefined from "./components/Pages/NotFound";

export default function App() {
    return (
        <Routes>
            <Route path="/:lang?/" Component={Layout}>
                <Route index Component={Main} />
                <Route path="questions/" Component={Main} />
                <Route path="tags/" Component={Main} />
            </Route>
            <Route path="*" Component={Undefined} />
        </Routes>
    );
}

// Layout.jsx
import { Outlet, useParams } from "react-router-dom";
import Header from "../Header/Header";
import Footer from "../Footer/Footer";
import Navigation from "../navigation/navigation";
import Undefined from "../Pages/NotFound";

function Layout() {
    const supportedLanguages = ["en", "ru", "uk"];
    const lang = useParams().lang || "en";
    
    if (!supportedLanguages.includes(lang)) {
        return <Undefined />;
    }

    return (
        <div className="flex column">
            <Header />
            <div className="flex">
                <Navigation />
                <Outlet />
            </div>
            <Footer />
        </div>
    );
}

export default Layout;

// Navigation.jsx
import { NavLink, useParams } from "react-router-dom";
import styles from "./navigation.module.css";

function Navigation() {
    const supportedLanguages = ["en", "ru", "uk"];
    const lang = useParams().lang || "en";
    
    if (!supportedLanguages.includes(lang)) {
        return null;
    }

    const navLinks = [
        { to: `/${lang}/`, label: "Главная", end: true },
        { to: `/${lang}/tags/`, label: "Теги", end: false },
        { to: `/${lang}/questions/`, label: "Вопросы", end: false }
    ];

    return (
        <aside className={styles.asideContainer}>
            {navLinks.map(({ to, label, end }) => (
                <NavLink
                    key={to}
                    to={to}
                    end={end}
                    className={({ isActive}) => 
                        isActive ? styles.active : styles.inactive
                    }
                >
                    {label}
                </NavLink>
            ))}
        </aside>
    );
}

export default Navigation;

Ключевые улучшения в этой реализации:

  1. Все определения маршрутов последовательно используют конечные слэши
  2. Ссылки навигации автоматически включают конечные слэши
  3. Свойство end используется правильно для точного соответствия
  4. Параметр языка правильно проверяется
  5. Последовательная структура путей во всем приложении

Это решение гарантирует, что все языковые ссылки автоматически включают конечные слэши, сохраняя правильное сопоставление маршрутов и подсветку навигации в React Router v6+.


Источники

  1. React Router - Обновление с v5
  2. React Router - Обсуждение поведения конечных слэшей
  3. O’Reilly - Краткое руководство React Router
  4. Stack Overflow - Поведение NavLink exact vs end в React Router
  5. Stack Overflow - Использование свойств exact и strict
Авторы
Проверено модерацией
Модерация