Ми вже вміємо будувати повноцінні додатки. Але що робити зі складними сценаріями? Авторизація, оптимізація повільних запитів, інтерактивність без переходу на нову сторінку.
Найпопулярніше питання: "Як закрити сторінку від неавторизованих користувачів?".
Ми створимо компонент-обгортку 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: [...]
}
Іноді 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>
)
}
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>
)
}
React Router "з коробки" має компонент <ScrollRestoration />. Він емулює поведінку браузера:
Просто додайте його у ваш RootLayout.
import { ScrollRestoration } from 'react-router-dom'
export default function RootLayout() {
return (
<div>
<Outlet />
<ScrollRestoration />
</div>
)
}
Ви пройшли шлях від розуміння "навіщо потрібен роутер" до використання Data APIs та оптимізації продуктивності. React Router — це більше, ніж просто бібліотека. Це фреймворк всередині бібліотеки, який диктує архітектуру вашого додатку.
Що далі?
createBrowserRouter).errorElement для створення надійних інтерфейсів.Успіхів у кодингу!