Дочерние маршруты

Последнее обновление: 26.10.2022

В рамках маршрутов в React можно определять дочерние маршруты. Такие подмаршруты будут отсчитываться от главного маршрута. Но для построения подобной системы есть ряд подходов. Рассмотрим их.

Определение подмаршрутов в коде компонента

Для применения подмаршрутов возьмем из прошлой темы веб-страницу index.html и изменим ее следующим образом:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Маршруты в React</title>
</head>
<body>
    <div id="app"></div>
           
    <script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
    <script src="https://unpkg.com/history@5/umd/history.production.min.js" crossorigin></script>
    <script src="https://unpkg.com/react-router@6.3.0/umd/react-router.production.min.js" crossorigin></script>
    <script src="https://unpkg.com/react-router-dom@6.3.0/umd/react-router-dom.production.min.js" crossorigin></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
    const Router = ReactRouterDOM.BrowserRouter;
    const Route = ReactRouterDOM.Route;
    const Routes = ReactRouterDOM.Routes;
   
    function Phone(){ return <h3>Смартфоны</h3>; }
    function Tablet(){ return <h3>Планшеты</h3>; }

    function Products(){
        return <div>
            <h2>Товары</h2>
                <Routes>
                    <Route path="/phones" element={<Phone />} />
                    <Route path="/tablets" element={<Tablet />} />
                </Routes>
            </div>;
    }
    ReactDOM.createRoot(
        document.getElementById("app")
    )
    .render(
        <Router>
            <div>
              <Routes>
                <Route path="/" element={<h2>Главная</h2>} />
                <Route path="/products/*" element={<Products />} />
                <Route path="*" element={<h2>Ресурс не найден</h2>} />
               </Routes>
            </div>
        </Router>
    );
    </script>
</body>
</html>

Для обработки запроса "/products" здесь определен маршрут, который обрабатывается компонентом Products:

<Route path="/products/*" element={<Products />} />

Обратите внимание на шаблон пути: path="/products/*". Символ * указывает, что компонент Products будет обрабатывать маршруты, которые начинаются "/products/", но после слеша также могут идти и другие символы.

Но сам этот компонент имеет вложенные маршруты:

function Products(){
	return <div>
			<h2>Товары</h2>
			<Routes>
                <Route path="/phones" element={<Phone />} />
                <Route path="/tablets" element={<Tablet />} />
            </Routes>
        </div>;
}

Вложенные маршруты отсчитываются фактически от главного маршрута "/products". То есть маршрут

<Route path="/phones" element={<Phone />} />

будет обрабатывать запросы по пути "/phones", который добавляется к пути главного компонента - "/products", то есть в итоге по пути "/products/phones". Аналогичным образом запросы по пути "/products/tablets" будут обрабатываться компонентом Tablet.

Запустим приложение и пройдемся по разным адресам:

Дочерние маршруты в React Вложенные маршруты в React

И что также можно заметить, то в обоих случаях выводится заголовок "Товары", так как он определен на главном компоненте Products. Остальное содержимое будет отличаться в зависимости от выбранного для обработки запроса компонента.

Аналогичный пример с использованием классов:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Маршруты в React</title>
</head>
<body>
    <div id="app"></div>
          
    <script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
    <script src="https://unpkg.com/history@5/umd/history.production.min.js" crossorigin></script>
	<script src="https://unpkg.com/react-router@6.3.0/umd/react-router.production.min.js" crossorigin></script>
	<script src="https://unpkg.com/react-router-dom@6.3.0/umd/react-router-dom.production.min.js" crossorigin></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
          
    <script type="text/babel">
    const Router = ReactRouterDOM.BrowserRouter;
    const Route = ReactRouterDOM.Route;
    const Routes = ReactRouterDOM.Routes;
  
    class Phone extends React.Component{
        render(){
            return <h3>Смартфоны</h3>;
        }
    }
    class Tablet extends React.Component{
        render(){
            return <h3>Планшеты</h3>;
        }
    }
    class Products extends React.Component{
        render(){
            return <div>
                        <h2>Товары</h2>
                        <Routes>
                            <Route path="/phones" element={<Phone />} />
                            <Route path="/tablets" element={<Tablet />} />
                        </Routes>
                    </div>;
        }
    }
    ReactDOM.createRoot(
        document.getElementById("app")
    )
    .render(
        <Router>
            <div>
              <Routes>
                <Route path="/" element={<h2>Главная</h2>} />
                <Route path="/products/*" element={<Products />} />
                <Route path="*" element={<h2>Ресурс не найден</h2>} />
               </Routes>
            </div>
        </Router>
    );
    </script>
</body>
</html>

Однако данный подход имеет как минимум один недостаток - при обращении к любым адресам, которые начинаются с "products", запросы будет обрабатывать компонент Products. Но, к примеру, мы хотим, чтобы он обрабатывал свой основной маршрут и запросы по дочерним маршутам. Поэтому рассмотрим другой подход.

Outlet

При другом подходе маршруты определяются вне компонента, а их содержимое вставляется в главный компонент с помощью встроенного компонента Outlet. Так, изменим предыдущий пример следующим образом:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Маршруты в React</title>
</head>
<body>
    <div id="app"></div>
          
    <script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
    <script src="https://unpkg.com/history@5/umd/history.production.min.js" crossorigin></script>
	<script src="https://unpkg.com/react-router@6.3.0/umd/react-router.production.min.js" crossorigin></script>
	<script src="https://unpkg.com/react-router-dom@6.3.0/umd/react-router-dom.production.min.js" crossorigin></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
          
    <script type="text/babel">
    const Router = ReactRouterDOM.BrowserRouter;
    const Route = ReactRouterDOM.Route;
    const Routes = ReactRouterDOM.Routes;
	const Outlet = ReactRouterDOM.Outlet;
  
    function Phone(){ return <h3>Смартфоны</h3>;}
    function Tablet(){ return <h3>Планшеты</h3>; }
    function Products(){
        return <div>
                    <h2>Товары</h2>
                    <Outlet />
                </div>;
    }
    ReactDOM.createRoot(
        document.getElementById("app")
    )
    .render(
        <Router>
            <div>
              <Routes>
                <Route path="/" element={<h2>Главная</h2>} />
                <Route path="/products" element={<Products />}>
					<Route path="phones" element={<Phone />} />
                    <Route path="tablets" element={<Tablet />} />
				</Route>
                <Route path="*" element={<h2>Ресурс не найден</h2>} />
               </Routes>
            </div>
        </Router>
    );
    </script>
</body>
</html>

Здесь надо отметить два момента. Прежле всего в коде компонента Products применяется компонент Outlet:

function Products(){
	return <div>
			<h2>Товары</h2>
			<Outlet />
		</div>;
}

Здесь вместо элемента <Outlet /> будет вставляться содержимое компонентов, которые обрабатывают дочерние маршруты.

Во-вторых, дочерние маршруты определены внутри основного маршрута:

<Route path="/products" element={<Products />}>
	<Route path="phones" element={<Phone />} />
	<Route path="tablets" element={<Tablet />} />
</Route>

Стоит отметить, что пути в дочерних маршрутах не должны начинаться со слеша. И в итоге дочерние маршруты также будут отсчитываються от главного маршрута "/products" и сопвадать с запросами "/products/phones" и "/products/tablets". Запрос по основному маршруту "/products" будет обрабатываться только компонентом Products. Все остальные запросы, которые не совпадают с основным и подмаршрутами, например, "/products/abc", будет обрабатываться самым последним маршрутом.

Аналогичный пример с использованием компонентов-классов:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Маршруты в React</title>
</head>
<body>
    <div id="app"></div>
          
    <script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
    <script src="https://unpkg.com/history@5/umd/history.production.min.js" crossorigin></script>
	<script src="https://unpkg.com/react-router@6.3.0/umd/react-router.production.min.js" crossorigin></script>
	<script src="https://unpkg.com/react-router-dom@6.3.0/umd/react-router-dom.production.min.js" crossorigin></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
          
    <script type="text/babel">
    const Router = ReactRouterDOM.BrowserRouter;
    const Route = ReactRouterDOM.Route;
    const Routes = ReactRouterDOM.Routes;
	const Outlet = ReactRouterDOM.Outlet;
  
    class Phone extends React.Component{
        render(){
            return <h3>Смартфоны</h3>;
        }
    }
    class Tablet extends React.Component{
        render(){
            return <h3>Планшеты</h3>;
        }
    }
    class Products extends React.Component{
        render(){
            return <div>
                        <h2>Товары</h2>
                        <Outlet />
                    </div>;
        }
    }
    ReactDOM.createRoot(
        document.getElementById("app")
    )
    .render(
        <Router>
            <div>
              <Routes>
                <Route path="/" element={<h2>Главная</h2>} />
                <Route path="/products" element={<Products />}>
					<Route path="phones" element={<Phone />} />
                    <Route path="tablets" element={<Tablet />} />
				</Route>
                <Route path="*" element={<h2>Ресурс не найден</h2>} />
               </Routes>
            </div>
        </Router>
    );
    </script>
</body>
</html>

Главный маршрут

Нередко возникает необходимость среди дочерних маршрутов выделить основной подмаршрут. Для установки такого подмаршрута применяется атрибут index. Например, изменим возьмем пример выше и изменим определение маршрутов:

<Router>
	<div>
		<Routes>
            <Route path="/" element={<h2>Главная</h2>} />
            <Route path="/products" element={<Products />}>
				<Route index element={<h3>Каталог товаров</h3>} />
				<Route path="phones" element={<Phone />} />
                <Route path="tablets" element={<Tablet />} />
			</Route>
            <Route path="*" element={<h2>Ресурс не найден</h2>} />
        </Routes>
    </div>
</Router>,

В данном случае для маршрута с путем "/products" определен основной подмаршрут

<Route index element={<h3>Каталог товаров</h3>} />

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

Outlet в React
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850