Защищенные маршруты с Layout

В приложениях на базе React с использованием Ant Design (antd) часто требуется разделять интерфейс на публичные и защищенные части. Для реализации защищенных маршрутов применяется комбинация React Router и компонентов Layout из Ant Design, что позволяет централизованно управлять структурой страниц и правами доступа.


Структура Layout в Ant Design

Ant Design предоставляет гибкий компонент Layout, включающий следующие подкомпоненты:

  • Layout.Header — шапка приложения, обычно содержит навигацию и лого.
  • Layout.Sider — боковая панель, часто используется для меню.
  • Layout.Content — основной контент страницы.
  • Layout.Footer — нижний колонтитул.

Пример базовой структуры:

import { Layout, Menu } from 'antd';
const { Header, Sider, Content } = Layout;

function AppLayout({ children }) {
  return (
    <Layout style={{ minHeight: '100vh' }}>
      <Sider>
        <Menu theme="dark" mode="inline">
          <Menu.Item key="1">Dashboard</Menu.Item>
          <Menu.Item key="2">Profile</Menu.Item>
        </Menu>
      </Sider>
      <Layout>
        <Header style={{ background: '#fff' }}>My App</Header>
        <Content style={{ margin: '16px' }}>{children}</Content>
      </Layout>
    </Layout>
  );
}

Ключевой момент: все защищенные страницы оборачиваются в один Layout, что позволяет централизованно управлять навигацией и стилями.


Реализация защищенных маршрутов

Для организации защищенных маршрутов создается специальный компонент, проверяющий авторизацию пользователя:

import { Navigate } from 'react-router-dom';

function ProtectedRoute({ isAuthenticated, children }) {
  if (!isAuthenticated) {
    return <Navigate to="/login" replace />;
  }
  return children;
}
  • isAuthenticated — логическое значение, определяющее, вошел ли пользователь в систему.
  • Navigate — компонент React Router, который выполняет перенаправление на страницу входа, если пользователь не авторизован.

Интеграция ProtectedRoute с Layout

Использование защищенного маршрута вместе с Layout выглядит так:

import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';

function App({ isAuthenticated }) {
  return (
    <Router>
      <Routes>
        <Route
          path="/dashboard"
          element={
            <ProtectedRoute isAuthenticated={isAuthenticated}>
              <AppLayout>
                <DashboardPage />
              </AppLayout>
            </ProtectedRoute>
          }
        />
        <Route path="/login" element={<LoginPage />} />
      </Routes>
    </Router>
  );
}

Преимущество такого подхода: все защищенные страницы используют один и тот же Layout, а логика проверки доступа сосредоточена в ProtectedRoute.


Динамическое меню в Sider

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

const menuItems = [
  { key: 'dashboard', label: 'Dashboard', roles: ['admin', 'user'] },
  { key: 'admin', label: 'Admin Panel', roles: ['admin'] }
];

function Sidebar({ userRole }) {
  const filteredItems = menuItems.filter(item => item.roles.includes(userRole));

  return (
    <Menu theme="dark" mode="inline" items={filteredItems} />
  );
}

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


Управление состоянием авторизации

Для крупных приложений логичнее хранить статус авторизации в глобальном состоянии, например, через Redux или Context API:

import { createContext, useContext } from 'react';

const AuthContext = createContext();

export function useAuth() {
  return useContext(AuthContext);
}

export function AuthProvider({ children }) {
  const [isAuthenticated, setIsAuthenticated] = useState(false);

  const login = () => setIsAuthenticated(true);
  const logout = () => setIsAuthenticated(false);

  return (
    <AuthContext.Provider value={{ isAuthenticated, login, logout }}>
      {children}
    </AuthContext.Provider>
  );
}

Использование контекста позволяет автоматически обновлять интерфейс при изменении прав доступа и легко масштабировать защиту маршрутов.


Адаптивность и стилизация Layout

Ant Design поддерживает адаптивный Sider с пропом collapsedWidth и управлением состоянием через breakpoint:

<Sider
  breakpoint="lg"
  collapsedWidth="80"
  onBreakpo int={broken => console.log(broken)}
  onColla pse={(collapsed, type) => console.log(collapsed, type)}
>
  <Menu theme="dark" mode="inline" />
</Sider>
  • breakpoint — точка, при которой Sider автоматически меняет состояние.
  • collapsedWidth — ширина боковой панели в свернутом состоянии.

Это важно для поддержания удобной навигации на мобильных устройствах и планшетах.


Совмещение Layout с Breadcrumb и PageHeader

Для улучшения UX рекомендуется использовать Breadcrumb и PageHeader внутри Content:

import { PageHeader, Breadcrumb } from 'antd';

function PageWrapper({ title, breadcrumbItems, children }) {
  return (
    <div>
      <Breadcrumb>
        {breadcrumbItems.map(item => (
          <Breadcrumb.Item key={item}>{item}</Breadcrumb.Item>
        ))}
      </Breadcrumb>
      <PageHeader title={title} />
      <div>{children}</div>
    </div>
  );
}

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


Выводы по архитектуре

  • Защищенные маршруты оборачиваются в ProtectedRoute для централизованной проверки доступа.
  • Все защищенные страницы используют общий Layout, обеспечивая единый стиль и навигацию.
  • Динамическое формирование меню и использование глобального состояния позволяют легко управлять доступом.
  • Интеграция Breadcrumb и PageHeader улучшает пользовательский опыт и ориентирование в приложении.
  • Адаптивные свойства Sider обеспечивают удобную навигацию на разных устройствах.

Такой подход формирует чистую, масштабируемую и поддерживаемую архитектуру для приложений на React с Ant Design.