Просунуті Патерни
Просунуті Патерни
Ми вже вміємо будувати повноцінні додатки. Але що робити зі складними сценаріями? Авторизація, оптимізація повільних запитів, інтерактивність без переходу на нову сторінку.
1. Захищені Маршрути (Protected Routes)
Найпопулярніше питання: "Як закрити сторінку від неавторизованих користувачів?".
Ми створимо компонент-обгортку RequireAuth, який перевіряє наявність юзера. Якщо його немає — перенаправляє на логін, запам'ятовуючи, звідки він прийшов.
import { Navigate, useLocation } from 'react-router-dom'
import { useAuth } from './auth-context' // Ваш хук авторизації
export default function RequireAuth({ children }) {
const { user } = useAuth()
const location = useLocation()
if (!user) {
// Redirect to login page, but save the current location they were trying to go to
return <Navigate to="/login" state={{ from: location }} replace />
}
return children
}
Використання в роутері:
{
path: "/dashboard",
element: (
<RequireAuth>
<DashboardLayout />
</RequireAuth>
),
children: [...]
}
2. Потокова передача даних (Defer)
Іноді loader завантажує дані занадто довго. Наприклад, на сторінці дашборду інформація про користувача вантажиться швидко (50мс), а графік статистики — довго (2с).
За замовчуванням loader чекатиме все. Користувач буде дивитися на порожній екран 2 секунди.
Ми можемо використати defer та компонент <Await>, щоб показати швидкі дані одразу, а повільні — довантажити.
import { defer, Await, useLoaderData } from 'react-router-dom'
import { Suspense } from 'react'
// Loader запускає обидва запити, але не чекає на повільний
export async function dashboardLoader() {
const userPromise = fetchUser() // швидкий (await тут, якщо критично)
const statsPromise = fetchStats() // повільний (не чекаємо тут)
return defer({
user: await userPromise, // чекаємо критичні дані
stats: statsPromise, // віддаємо проміс
})
}
export default function Dashboard() {
const data = useLoaderData()
return (
<div>
<h1>Вітаємо, {data.user.name}!</h1>
<Suspense fallback={<p>Завантаження статистики...</p>}>
<Await resolve={data.stats}>{(stats) => <StatsChart data={stats} />}</Await>
</Suspense>
</div>
)
}
3. useFetcher: Інтерактивність без навігації
Іноді нам треба викликати action, але не переходити на іншу сторінку.
Класичні приклади:
- Кнопка "Лайк".
- Додавання товару в кошик.
- Підписка на розсилку у футері.
Для цього є useFetcher.
import { useFetcher } from 'react-router-dom'
function NewsletterSignup() {
const fetcher = useFetcher()
const isSubmitting = fetcher.state === 'submitting'
// fetcher.Form не змінює URL!
return (
<fetcher.Form method="post" action="/newsletter">
<input type="email" name="email" placeholder="Ваш email" />
<button disabled={isSubmitting}>{isSubmitting ? 'Підписуємо...' : 'Підписатися'}</button>
{/* Показуємо повідомлення про успіх після завершення */}
{fetcher.data && <p>Дякуємо за підписку!</p>}
</fetcher.Form>
)
}
4. Scroll Restoration
React Router "з коробки" має компонент <ScrollRestoration />. Він емулює поведінку браузера:
- При натисканні "Назад" — скрол повертається туди, де він був.
- При натисканні на посилання — скрол стрибає нагору.
Просто додайте його у ваш RootLayout.
import { ScrollRestoration } from 'react-router-dom'
export default function RootLayout() {
return (
<div>
<Outlet />
<ScrollRestoration />
</div>
)
}
Фінал
Ви пройшли шлях від розуміння "навіщо потрібен роутер" до використання Data APIs та оптимізації продуктивності. React Router — це більше, ніж просто бібліотека. Це фреймворк всередині бібліотеки, який диктує архітектуру вашого додатку.
Що далі?
- Спробуйте переписати свій старий проект на Data Router (
createBrowserRouter). - Експериментуйте з
errorElementдля створення надійних інтерфейсів. - Поглибте знання про HTTP кешування, оскільки React Router тісно з ним інтегрований.
Успіхів у кодингу!