ВСТУП
Ви створили вебдодаток: гарний, анімований, швидкий і продуктивний, з величезною купою крутих фіч. Але уявіть собі таку проблему: незалогінений користувач спокійнесенько собі відкриває сторінку профілю користувача, інший незалогінений користувач вводить в URL-адресу чужий id і переходить на чужий профіль. От халепа. Такого точно не має бути.
Сьогодні ми хочемо трохи прочинити для вас завісу до того, що таке роутинг у React і разом розібратися, як це працює, опираючись на те, як працює маршрутизація у Skillzzy.
ЗАГАЛЬНИЙ ПОГЛЯД НА РОУТИНГ
Почнемо з того, що таке той роутинг (далі іноді буде вживатися термін маршрутизація). Можемо собі в голові намалювати просту схему будинку, в якому є різні кімнати. Для того, щоб потрапити з однієї кімнати в іншу нам потрібні двері (ну або дірки в стінах, як мінімум).
URL-адреса в браузері — це ці самі двері, а роутинг — це така штука, яка каже: «Коли користувач перейшов за шляхом “/livingroom”, то йому потрібно відобразити компонент вітальні».
Отакі от асоціації будинку з програмуванням:) Далі будемо розбиратися на більш звичних компонентах вебдодатка.
Сам по собі React поняття не має що таке /profile, /about, /faq і будь-яка інша сторінка. Всю магію тут якраз робить React Router. Це найбільш популярна бібліотека для виконання цієї задачі, тож і розглядати все будемо на її прикладі.
ОСНОВНІ ЗАДАЧІ REACT ROUTER
- відстежувати URL-адресу в браузері,
- відображати користувачу потрібний компонент, відповідно до тієї самої URL-адреси,
- виконувати попередній пункт без перезавантаження всієї сторінки, а тільки точково.
Нижче ми можемо оглянути, як виглядає використання React Router у проєкті.
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import HomePage from './pages/HomePage';
import ProfilePage from './pages/ProfilePage';
const App = () => (
<BrowserRouter>
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/profile" element={<ProfilePage />} />
</Routes>
</BrowserRouter>
);
export default App;
Цей приклад доступно показує, як React Router дозволяє створювати маршрути / та /profile, які відображають два різних компоненти. А тепер повернімося до питання із захистом таких сторінок, як сторінка користувача тощо.
PROTECTED ROUTES
Protected Routes — це захищені сторінки, які не пропускають користувачів, які не відповідають певним умовам для доступу:
- Користувач авторизований?
- Користувач з цією роллю має доступ до запитуваної сторінки?
- Користувач хоче увійти у свій профіль?
- Чи є у користувача всі необхідні дані для переходу на цю сторінку?
Якщо користувач не відповідає умовам, то перенаправляється на іншу сторінку (наприклад, сторінку логіна). Простіше кажучи: це такі собі сторінки під замком.
Зазвичай у вебдодатках може бути декілька захищених сторінок: профіль користувача, сторінка з налаштуваннями, розділ додатка, куди мають доступ тільки адміністратори, сторінка керування додатком тощо.
ПРИКЛАД РЕАЛІЗАЦІЇ PROTECTED ROUTES
Як ви розумієте, Protected Routes будуються трохи по іншому, тому нам варто звернути увагу на різницю реалізації звичайних роутів і захищених роутів.
1️⃣ Простий приклад перевірки авторизації
Найпростіший приклад — перевірка, чи користувач авторизований:
import { Route, Navigate } from 'react-router-dom';
const ProtectedRoute = ({ element, isAuthenticated }) => {
return isAuthenticated ? element : <Navigate to="/login" />;
};
export default ProtectedRoute;
У цьому прикладі, якщо користувач авторизований (isAuthenticated === true), маршрут відкривається. В іншому випадку його перенаправляють на сторінку входу /login.
2️⃣ Приклад із ролями користувачів
На нашій платформі у користувачів є ролі. І звичайний собі user не повинен мати доступ до компонентів і сторінок, до яких мають доступ адміністратори.
Якщо потрібно перевірити роль користувача (наприклад, admin):
const RoleBasedRoute = ({ element, userRole, requiredRole }) => {
return userRole === requiredRole ? element : <Navigate to="/forbidden" />;
};
export default RoleBasedRoute;
Цей підхід підходить для обмеження доступу до сторінок для адміністраторів або інших ролей.
3️⃣ Реалізація маршруту для профілю користувача
Іноді можуть траплятися складніші ситуації, наприклад, захист профілю користувача. Тобто потрібно перевірити, чи намагається користувач отримати доступ до профілю через чужий userId. Ось такий це має вигляд:
import { useParams, Navigate } from 'react-router';
import { useSelector } from 'react-redux';
import UserProfilePage from '../pages/ProfilePages/UserProfilePage';
const UserProfileRoute = () => {
const { userId } = useParams();
const { id: authorizedUserId } = useSelector((state) => state.auth.user.data);
if (+userId === authorizedUserId) {
return <Navigate replace to="/profile" />;
}
return <UserProfilePage />;
};
export default UserProfileRoute;
4️⃣ Реалізація Protected Routes у реальному додатку
Цей приклад демонструє реальний випадок зі складнішою маршрутизацією. У цьому випадку ми маємо:
- Захищені маршрути, обгорнуті в компонент RequireAuth.
- Вкладені маршрути для профілю (PersonalProfilePage і UserProfileRoute).
Додаткові маршрути для сторінок, таких як «Спеціалізації», «Розклад», «FAQ».
import { createBrowserRouter } from 'react-router'; // React Router v6
import ErrorPage from '../pages/ErrorPage'; // Сторінка помилки
import HomePage from '../pages/HomePage/HomePage'; // Головна сторінка
import RequireAuth from '../redux/auth/RequireAuth.jsx'; // Компонент авторизації
import PersonalProfilePage from '../pages/ProfilePages/PersonalProfilePage'; // Профіль користувача
import SpecializationPage from '../pages/SpecializationPage'; // Спеціалізації
import SchedulePage from '../pages/ShedulePage'; // Розклад
import FaqPage from '../pages/FaqPage'; // FAQ
import PrivacyPolicy from '../pages/PrivacyPolicy'; // Політика конфіденційності
import TermsAndConditions from '../pages/TermsAndConditions'; // Умови використання
import UserProfileRoute from './UserProfileRoute.jsx'; // Захищений маршрут профілю іншого користувача
import navigationLinks from './links'; // Конфігурація шляхів
/**
* Основний маршрутизатор додатку.
* - Використовує вкладені маршрути для логічної структури.
* - Захищені маршрути обгорнуті у RequireAuth для перевірки авторизації.
* - Обробляє маршрути для різних сторінок (Профіль, Розклад, FAQ тощо).
*/
const router = createBrowserRouter([
{
path: navigationLinks.home, // Головна сторінка
errorElement: <ErrorPage />, // Відображення сторінки помилки
children: [
{
index: true, // Домашній маршрут
element: <HomePage />,
},
{
path: navigationLinks.privacy_policy, // Політика конфіденційності
element: <PrivacyPolicy />,
},
{
path: navigationLinks.terms_and_conditions, // Умови використання
element: <TermsAndConditions />,
},
{
element: <RequireAuth />, // Захищені маршрути
children: [
{
path: navigationLinks.profile, // Профіль поточного користувача
element: <PersonalProfilePage />,
},
{
path: `${navigationLinks.profile}/:userId`, // Профіль іншого користувача
element: <UserProfileRoute />, // Захищений маршрут
},
{
path: navigationLinks.schedule, // Розклад
element: <SchedulePage />,
},
{
path: navigationLinks.specializations, // Спеціалізації
element: <SpecializationPage />,
},
{
path: navigationLinks.faq, // FAQ
element: <FaqPage />,
},
],
},
],
},
]);
export default router;
АРХІТЕКТУРА ЗАХИЩЕНИХ МАРШРУТІВ У НАШОМУ ПРОЄКТІ
Тепер зазирнімо за лаштунки проєкту Skillzzy і подивимося, як ми організували захищені маршрути. Отже, ards, де ми тримаємо спільні інструменти для захисту маршрутів. Який це має вигляд у дії?
СТРУКТУРА МАРШРУТІВ
Усе починається з головного файлу router.jsx, який ми вже розглядали вище. Це наш «диригент», який керує всіма маршрутами додатка. Але захист сторінок ми винесли в окремі файли, щоб усе було організовано й не плуталося під ногами.
У папці guards лежать спеціальні компоненти-гарди, які відповідають за захист окремих сторінок:
- scheduleInterviewsGuard.jsx — стежить, щоб на сторінку розкладу потрапляли лише ті, хто має на це право.
- userProfileGuard.jsx — перевіряє, чи користувач дивиться свій профіль, а не чужий (ми вже бачили приклад із UserProfileRoute).
А в папці sharedGuards ми тримаємо спільні інструменти, які використовуються в різних гардах. Наприклад, там може бути логіка перевірки авторизації чи ролей, щоб не дублювати код.
ЯК ЦЕ ПРАЦЮЄ?
Кожен гард — це окремий компонент, який обгортає маршрут і вирішує: пускати користувача чи ні. Наприклад, userProfileGuard.jsx перевіряє userId із URL і порівнює його з ID авторизованого користувача. Якщо вони збігаються, то користувача перенаправляє на його власний профіль (/profile), а якщо ні — показує сторінку іншого користувача (але лише якщо є доступ).
У router.jsx ми обгортаємо всі захищені маршрути в компонент RequireAuth, який робить першу перевірку: чи користувач узагалі залогінений? Якщо ні — кидає його на сторінку логіна. А вже всередині RequireAuth працюють наші гарди, які додають додаткові перевірки для кожної сторінки.
СХЕМА АРХІТЕКТУРИ
Уявіть собі таку схему:
- На верхньому рівні — router.jsx, це ніби головний вхід у будинок.
- Усередині є двері з табличкою RequireAuth — це охоронець, який питає: «Ти залогінений?».
- Якщо ти пройшов цю перевірку, то потрапляєш у коридор із дверима, де стоять інші охоронці:
- userProfileGuard стоїть перед дверима /profile/:userId і питає: «Це твій профіль?».
- scheduleInterviewsGuard охороняє /schedule і перевіряє, чи маєш ти доступ до розкладу.
- А в сусідній кімнаті sharedGuards лежать інструменти, які використовують усі охоронці: наприклад, функція перевірки ролей чи токена.
ЧОМУ ТАК ЗРУЧНО?
Така архітектура дозволяє нам:
- Тримати код чистим і модульним — кожен гард відповідає за свою сторінку.
- Легко додавати нові захищені маршрути — просто створюємо новий гард і додаємо його в router.jsx.
- Повторно використовувати спільну логіку через sharedGuards, щоб не писати одне й те саме сто разів.
Тож у нашому проєкті захищені маршрути — це не просто замки, а ціла система безпеки з охоронцями, які працюють разом, щоб ніхто не зайшов туди, куди не треба!
ВИСНОВОК
Protected Routes у React — це як замок на дверях у твій крутий вебдодаток. За допомогою React Router і кількох хитрих перевірок (авторизований юзер чи ні, своя сторінка чи чужа) ти можеш спокійно закрити доступ туди, куди незалогіненим або хитрим користувачам потикатися не варто.
Ми розібрали, як це працює — від простеньких прикладів із ProtectedRoute до серйозної маршрутизації, як у Skillzzy. Такі штуки не лише роблять твій додаток безпечнішим, а й додають йому шарму — бо хто ж не любить, коли все під контролем? Тож бери й захищай свої маршрути, щоб ніхто не гуляв там, де не треба!