Mundarija (36)
- Bu darsda
- 1. Nega bu kerak?
- 2. O'rnatish va narxi
- 2.1 Bitta paket: react-router
- 2.2 Bepul emas: +29.4 KB
- 3. Uch rejim
- 4. Birinchi router — data mode
- 4.1 Marshrut obyekti
- 4.2 Natija oynasida: createMemoryRouter
- 4.3 Loyihada: createBrowserRouter
- 5. Link: sahifa yuklanmasdan o'tish
- 5.1 Link va oddiy <a>
- 5.2 Link ning foydali prop'lari
- 6. Umumiy qism: layout va <Outlet />
- 7. NavLink: qaysi sahifadaman?
- 7.1 aria-current va active
- 7.2 Uslub: atributga qarab
- 7.3 end: ichma-ich yo'l tuzog'i
- 8. Declarative mode: <Routes> va <Route>
- 9. Ko'p uchraydigan xatolar
- 9.1 Link router'dan tashqarida
- 9.2 Boshqa xatolar
- 10. Vazifalar qadami: uch sahifa (holat 02)
- 10.1 Marshrutlar — src/marshrutlar.tsx
- 10.2 Router — src/main.tsx
- 10.3 Umumiy qism — sahifalar/Qobiq.tsx
- 10.4 Natija
- 10.5 Commit
- 11. Mashqlar
- 1-mashq (oson): bo'sh joylarni to'ldiring
- 2-mashq (o'rta): «Aloqa» sahifasi
- 3-mashq (qiyin): declarative → data mode
- 4-mashq: vazifalar ga nazar
- 12. Real ishda
- Xulosa
- Manbalar
React Router I: o'rnatish, marshrutlar, Link va NavLink
Qisqacha: React Router — React uchun eng ko'p ishlatiladigan router kutubxonasi.
npm i react-routerbilan o'rnatiladi (8-versiyadareact-router-dompaketi yo'q). Marshrutlar obyektlar massivi bilan yoziladi:{ path: "/menyu", element: <Menu /> }. UlardancreateBrowserRouter(marshrutlar)router yasaydi,<RouterProvider router={router} />uni chizadi. Sahifalar orasida<Link to="/menyu">bilan o'tiladi — sahifa qayta yuklanmaydi.<NavLink>esa joriy sahifa havolasigaaria-current="page"vaactiveklassini o'zi qo'yadi.
Bu darsda
- React Router 8.4.0 ni o'rnatasiz va uning bundle narxini raqam bilan bilasiz.
- Marshrutlarni obyekt sifatida yozib,
createMemoryRouter/createBrowserRouterbilan ulaysiz. LinkvaNavLinkbilan menyu yasaysiz, joriy sahifaniaria-currentbilan belgilaysiz.- Declarative va data mode farqini va kurs nega data mode'ni tanlaganini tushuntira olasiz.
vazifalarni uch sahifaga bo'lasiz: ro'yxat, statistika, sozlamalar — va commit qilasiz.
Oldin bilishingiz kerak: SPA routing qanday ishlaydi, Semver va versiya diapazonlari, React'da accessibility asoslari.
1. Nega bu kerak?
O'tgan darsda router'ni o'zimiz yozdik: manzil — state, Link — preventDefault va navigate, tarix — massiv. 40 qatorga sig'di. Lekin vazifalar ga yana ko'p narsa kerak bo'ladi:
/vazifa/2kabi parametrli manzillar;- umumiy qism (menyu) va uning ichida almashadigan sahifa;
- sahifa ochilishidan oldin ma'lumot yuklash, xatoni ushlash;
?filtr=faolni o'qish va yozish, sahifa almashganda fokus va aylantirish.
Bularning har birini o'zimiz yozsak — o'nlab tuzoq. Ctrl + bosish, ilova papkasi (/vazifalar/), «Orqaga» bosilganda qaysi yozuvga qaytish… React Router — shu ishlarni bajaradigan kutubxona. Uni Remix jamoasi yozadi, birinchi versiyasi 2014-yilda chiqqan. Bu darsda — asosi: o'rnatish, marshrutlar, havolalar.
Hayotiy o'xshatish: «Bahor» oshxonasining menyusi. Taom nomi (manzil) va oshpaz pishiradigan taom (sahifa) — juftlik. O'tgan darsda menyuni qo'lda, qog'ozga yozdik. Endi — tayyor buyurtma tizimi: menyu bir joyda, buyurtma qabul qilish, ofitsiant va kassa o'zi ishlaydi.
2. O'rnatish va narxi
2.1 Bitta paket: react-router
npm i --save-exact react-router@8.4.0package.json da:
"dependencies": {
"react": "19.3.0",
"react-dom": "19.3.0",
"react-router": "8.4.0",
"web-vitals": "6.2.3"
}--save-exact — ^ siz, aniq versiya (Semver). Kursda hamma kutubxona shunday. react-router 8.4.0 — 2026-09-15 da chiqqan. 8-versiya 2026-06-17 dan beri asosiy (latest), 7.x — faqat xavfsizlik tuzatishlari oladigan qo'llab-quvvatlash tarmog'i.
Diqqat: Eski maqola va videolarda
npm i react-router-domni ko'rasiz. 6-versiyagacha DOM qismi alohida paket edi. 7-versiyada hammasireact-routerga birlashdi,react-router-domfaqat eski importlar buzilmasin deb qoldirildi. 8-versiyadareact-router-domolib tashlangan (CHANGELOG, v8.0.0). Eski kodda ko'rsangiz:"react-router-dom"→"react-router",RouterProvider—"react-router/dom"dan yoki"react-router"dan.
2.2 Bepul emas: +29.4 KB
Kanonda o'lchandi: vazifalar brauzer yuklaydigan JS va CSS, gzip bilan:
- v8 (router yo'q)90,7 KB
- + React Router 7.18.4sinov120,4 KB
- + React Router 8.4.0 (kanon)120,1 KB
Manba: O'lchandi: vazifalar v8 va holat 02, gzip -9, natijalar/tekshiruv-00.txt, tekshiruv-02.txt, 02-hajm-rr7.txt, 2026-10-13
Router 29.4 KB qo'shdi — butun ilovaning uchdan biridan ko'pi. 7 va 8 versiyalar hajmi deyarli bir xil (120 402 va 120 120 bayt). Bu ikki narsani anglatadi. Birinchidan, router "tekin" emas: sekin internetda telefon bu baytlarni yuklaydi va bajaradi. Ikkinchidan, uch sahifali ilova uchun bu narx o'zini oqlaydi, chunki keyingi darslarda shu paketdan parametrlar, loader, xatolar va URL holati ham olinadi. Kichikroq muqobil — TanStack Router, o'sha darsda o'lchab solishtiramiz.
3. Uch rejim
React Router'ni uch xil ishlatish mumkin. Hujjat ularni rejim (mode) deydi. Har keyingisi oldingisiga imkoniyat qo'shadi:
| Rejim | Asosi | Nima beradi |
|---|---|---|
| Declarative | <BrowserRouter>, <Routes>, <Route> |
manzil → komponent, havolalar |
| Data | createBrowserRouter + <RouterProvider> |
+ loader, action, xato sahifasi |
| Framework | Vite plagini, routes.ts |
+ tiplar, kodni bo'lish, SSR |
Declarative mode — marshrutlar JSX ichida, komponentlar daraxtining bir qismi. Data mode — marshrutlar React'dan tashqarida, oddiy obyektlar massivi. Router ularni oldindan biladi, shuning uchun sahifa chizilishidan oldin ma'lumot yuklay oladi (data mode darsida). Framework mode — data mode ustiga Vite plagini va fayl konvensiyasi (framework mode darsida o'lchaymiz).
Kurs va vazifalar — data mode. Sabab: 5-darsdan loader va action kerak bo'ladi, declarative mode'da ular yo'q. Data mode'ning g'oyalari esa (marshrut obyekti, loader) Next.js qismida ham uchraydi. Declarative mode'ni ham ko'ramiz — eski loyihalarda u ko'p.
4. Birinchi router — data mode
4.1 Marshrut obyekti
Marshrut — oddiy obyekt. Hozircha ikki maydon:
path— manzil ("/menyu");element— shu manzilda chiziladigan JSX (<Menu />).
Ulardan massiv tuziladi va router yasaladi.
4.2 Natija oynasida: createMemoryRouter
O'tgan darsda aytganimizdek, natija oynasida manzil satri yo'q (about:srcdoc). Shuning uchun jonli misollarda — createMemoryRouter: tarix xotirada, xuddi biz yozgan entries va index kabi. Loyihada esa createBrowserRouter — keyingi bo'limda. Qolgan hamma narsa bir xil.
«Bahor»ning ikki sahifasi. Mehmon «Menyu» havolasini bosadi:
import { useEffect } from "react";
import {
createMemoryRouter,
Link,
RouterProvider,
} from "react-router";
function Home() {
useEffect(() => {
console.log(document.querySelector("a").outerHTML);
console.log("Home: ekranga chiqdi");
return () => console.log("Home: olib tashlandi");
}, []);
return (
<div>
<h1>Bahor oshxonasi</h1>
<Link to="/menyu">Menyu</Link>
</div>
);
}
function Menu() {
return (
<div>
<h1>Menyu</h1>
<p>Osh — 35 000 so'm</p>
<Link to="/">Bosh sahifa</Link>
</div>
);
}
// Natija oynasida manzil satri yo'q — xotiradagi router
const router = createMemoryRouter(
[
{ path: "/", element: <Home /> },
{ path: "/menyu", element: <Menu /> },
],
{ initialEntries: ["/"] },
);
export default function App() {
console.log("App chizildi");
return <RouterProvider router={router} />;
}Konsolda:
App chizildi
<a href="/menyu" data-discover="true">Menyu</a>
Home: ekranga chiqdi
Home: olib tashlandiEkranda:
Menyu
Osh — 35 000 so'm
Bosh sahifaKonsoldan to'rt narsa o'qiladi:
App chizildi— bir marta. Sahifa almashgandaAppqayta chizilmadi. Router o'z ichidagi state'ni o'zgartirdi va faqat sahifa qismini almashtirdi.Link— oddiy<a href="/menyu">. O'tgan darsdagiLinkimiz bilan bir xil g'oya: havola havola bo'lib qoladi.data-discover— framework mode uchun belgi (marshrutni oldindan topish), data mode'da ta'siri yo'q.Home: olib tashlandi— eski sahifa unmount bo'ldi: effekt tozalanishi ishladi (useEffect III). Sahifa ichidagi state ham yo'qoldi. Qaytib kelsangiz,Homenoldan o'rnatiladi.- Ekranda — faqat
Menu. Har manzilda bitta mos marshrutningelementi chiziladi.
initialEntries: ["/"] — boshlang'ich tarix: qaysi manzildan boshlash. Uni ["/menyu"] qilib «Ishga tushir» ni bosing — misol menyudan ochiladi. Bu chuqur havolaning xotiradagi o'xshashi.
Tekshirib ko'ring:
HomeichidauseStatebilan hisoblagich bo'lsa va uni 3 ga yetkazib, «Menyu» ga o'tib, qaytsangiz — hisoblagich nechada bo'ladi?
Javob
0 da. Sahifa almashganda Home daraxtdan olib tashlandi — uning state'i ham ketdi (State I: state komponent o'rnatilgan joyga bog'langan). Qaytganda Home yangidan o'rnatiladi. Sahifalar orasida saqlanishi kerak bo'lgan qiymat sahifadan yuqorida turishi kerak: umumiy qismda (layout), URL'da yoki store'da — bularni keyingi darslarda ko'ramiz.
4.3 Loyihada: createBrowserRouter
Haqiqiy loyihada main.jsx:
import { createRoot } from "react-dom/client";
import { createBrowserRouter } from "react-router";
import { RouterProvider } from "react-router/dom";
import { Home } from "./Home.jsx";
import { Menu } from "./Menu.jsx";
// Router — bir marta, komponentdan TASHQARIDA
const router = createBrowserRouter([
{ path: "/", element: <Home /> },
{ path: "/menyu", element: <Menu /> },
]);
createRoot(document.getElementById("root")).render(
<RouterProvider router={router} />,
);Uch izoh:
createBrowserRouter— tarix brauzerniki: manzil satri,pushState,popstate. Jonli misoldagicreateMemoryRouterbilan bir xil qabul qiladi, faqatinitialEntrieso'rniga hozirgi manzil (location) dan boshlaydi.- Router komponentdan tashqarida yaratiladi. Hujjat buni alohida yozadi: data router'ni React state'ida saqlamang. Komponent ichida bo'lsa, har renderda yangi router — tarix va holat yo'qoladi.
RouterProviderhujjatda"react-router/dom"dan. Bu variant React DOM'ningflushSyncini router'ga beradi (navigatsiyani sinxron bajarish imkoniyati uchun)."react-router"dagisi ham ishlaydi —vazifalarkanonida shunisi.
Diqqat: Natija oynasida
createBrowserRouter"Invalid URL" xatosi bilan yiqiladi: iframe manziliabout:srcdoc, undan haqiqiy yo'l yasab bo'lmaydi. Shuning uchun darsdagi har jonli misolcreateMemoryRouteryokiMemoryRouterda. O'z loyihangizga ko'chirganda bitta qatorni almashtirasiz.
5. Link: sahifa yuklanmasdan o'tish
5.1 Link va oddiy <a>
Link <a href> chizadi, lekin bosilganda router'ga o'tadi. Oddiy <a href="/menyu"> ni ilova ichida yozsangiz, ishlaydi — lekin sahifa butunlay qayta yuklanadi:
<Link to="/menyu"> |
<a href="/menyu"> |
|
|---|---|---|
| Serverga so'rov | yo'q | bor (butun HTML) |
| JS holati, keshdagi ma'lumot | saqlanadi | yo'qoladi |
| Ekran | faqat sahifa qismi almashadi | oq ekran, noldan |
Ilova ichidagi o'tishlar — doim Link. Boshqa saytga havola — oddiy <a>: Link absolyut manzilni (https://…) baribir oddiy havola kabi qoldiradi.
5.2 Link ning foydali prop'lari
| Prop | Nima qiladi |
|---|---|
to |
qayerga: "/menyu" yoki "/menyu?saralash=narx" |
replace |
tarixga yangi yozuv qo'shmaydi, joriysini almashtiradi |
state |
manzilga ko'rinmas qo'shimcha ma'lumot (history.state ga) |
reloadDocument |
ataylab oddiy yuklanish (masalan, boshqa ilovaga) |
replace History API darsidagi replaceState ning o'zi. Masalan, kirish formasidan keyin «Orqaga» yana formaga qaytarmasligi kerak — buni himoyalangan marshrutlar darsida ishlatamiz. Nisbiy to ("osh", "..") — ichma-ich marshrutlar darsida.
6. Umumiy qism: layout va <Outlet />
Menyu har sahifada takrorlanmasligi uchun uni bir joyga chiqaramiz. Marshrut children (bolalar) olishi mumkin. Ota marshrutning element i umumiy qismni chizadi, bola sahifa esa uning ichidagi <Outlet /> o'rnida chiziladi. Outlet — "rozetka": bola sahifa shu joyga ulanadi.
{
path: "/",
element: <Layout />, // menyu + <Outlet />
children: [
{ index: true, element: <Home /> }, // aynan "/"
{ path: "menyu", element: <Menu /> }, // "/menyu"
],
}index: true — ota manzilining o'zi (/). Bolaning path i / siz yoziladi — ota yo'liga qo'shiladi. Shunday ota — layout marshrut. Hozircha shu yetadi. Ichma-ich daraxt, layout va index ning hamma qoidalari — React Router II da.
7. NavLink: qaysi sahifadaman?
7.1 aria-current va active
Menyuda joriy sahifa ajralib turishi kerak. Ko'radigan odam uchun — qalin yozuv yoki chiziq. Malika (ekran o'quvchi) uchun — "joriy sahifa" degan so'z. Ikkalasini bitta atribut beradi: aria-current="page" (React'da accessibility). NavLink — joriy manzilga mos bo'lsa, shu atributni va active klassini o'zi qo'yadigan Link.
Quyidagi misolda CurrentLink — har sahifa almashganda DOM'dan aria-current li havolani topib, konsolga yozadi. U useLocation() dan foydalanadi: bu hook joriy manzilni beradi (to'liq — React Router III da):
import { useEffect } from "react";
import {
createMemoryRouter,
NavLink,
Outlet,
RouterProvider,
useLocation,
} from "react-router";
function CurrentLink() {
const { pathname } = useLocation();
useEffect(() => {
const link = document.querySelector('[aria-current="page"]');
console.log(pathname, "→", link.textContent, link.className);
}, [pathname]);
return null;
}
function Layout() {
return (
<div>
<nav aria-label="Bo'limlar">
<NavLink to="/" end>Bosh sahifa</NavLink>
{" | "}
<NavLink to="/menyu">Menyu</NavLink>
</nav>
<CurrentLink />
<Outlet />
</div>
);
}
const router = createMemoryRouter([
{
path: "/",
element: <Layout />,
children: [
{ index: true, element: <h1>Xush kelibsiz</h1> },
{ path: "menyu", element: <h1>Menyu: osh, manti</h1> },
],
},
]);
export default function App() {
return <RouterProvider router={router} />;
}Konsolda:
/ → Bosh sahifa active
/menyu → Menyu activeEkranda:
Bosh sahifa | Menyu
Menyu: osh, mantiBosishdan keyin aria-current va active «Bosh sahifa» dan «Menyu» ga ko'chdi — biz bitta if ham yozmadik. Menyu (Layout) esa joyida qoldi, faqat <Outlet /> o'rni almashdi.
7.2 Uslub: atributga qarab
CSS'da joriy havolani qanday tanlash kerak? Ikki yo'l bor: .active klassi yoki [aria-current="page"] atributi. Ikkinchisi yaxshiroq: vizual belgi va ekran o'quvchi uchun belgi — bitta manba. Biri bor, ikkinchisi yo'q bo'lib qolishi mumkin emas. vazifalar da aynan shunday:
.bolimlar a[aria-current="page"] {
font-weight: 700;
text-decoration-thickness: 3px;
}Klass kerak bo'lsa (masalan Tailwind), className ga funksiya berish mumkin: className={({ isActive }) => isActive ? "font-bold" : ""}. style va children ham shunday funksiya qabul qiladi. Funksiya oladigan obyektda isActive dan tashqari isPending ham bor — data mode'da sahifa yuklanayotganda true (loader'lar bilan, 5-darsda).
7.3 end: ichma-ich yo'l tuzog'i
NavLink sukut bo'yicha boshlanishi bo'yicha solishtiradi. /menyu havolasi /menyu da ham, /menyu/osh da ham joriy hisoblanadi. Bo'lim menyusi uchun bu to'g'ri: osh sahifasida turganda "Menyu" bo'limi belgilangan bo'lsin. Lekin ba'zan noto'g'ri. Uch havola — bo'lim, ichki sahifa va bosh sahifa. [ ] — joriy belgisi (children funksiyasi bilan):
import {
createMemoryRouter,
NavLink,
Outlet,
RouterProvider,
} from "react-router";
function Item({ to, end, children }) {
return (
<NavLink to={to} end={end}>
{({ isActive }) => (isActive ? `[${children}]` : children)}
</NavLink>
);
}
function Layout() {
return (
<div>
<nav>
<Item to="/">Bosh</Item> <Item to="/menyu">Menyu</Item>{" "}
<Item to="/menyu/osh">Osh</Item>
</nav>
<Outlet />
</div>
);
}
const router = createMemoryRouter([
{
path: "/",
element: <Layout />,
children: [
{ index: true, element: <h1>Bosh sahifa</h1> },
{ path: "menyu", element: <h1>Menyu</h1> },
{ path: "menyu/osh", element: <h1>Osh — 35 000 so'm</h1> },
],
},
]);
export default function App() {
return <RouterProvider router={router} />;
}Ekranda:
Bosh [Menyu] [Osh]
Osh — 35 000 so'm/menyu/osh da ikki havola joriy: «Menyu» ham, «Osh» ham. Ikkalasida aria-current="page" — Malika ikki "joriy sahifa"ni eshitadi. Agar «Menyu» faqat aynan /menyu da belgilanishi kerak bo'lsa — <Item to="/menyu" end>. end — "faqat manzil shu yerda tugasa".
«Bosh» esa /menyu/osh da belgilanmadi, garchi har manzil / bilan boshlansa ham. react-router 8.4.0 kodida ildiz (/) uchun alohida shart bor: / dan keyingi belgi ham / bo'lishi kerak. Baribir hujjat va kanon <NavLink to="/" end> yozadi — niyat aniq ko'rinadi va versiyaga bog'liq bo'lmaydi.
Tekshirib ko'ring: Misoldagi «Menyu» ga
endqo'shsangiz va «Osh» ni bossangiz, ekranning birinchi qatori qanday bo'ladi?
Javob
Bosh Menyu [Osh]. end bilan «Menyu» faqat /menyu da joriy, /menyu/osh da emas. Endi joriy havola — bitta, aria-current="page" ham bitta. Qaysi biri to'g'ri — dizayn qarori: "bo'lim" menyusi uchun end siz, "aniq sahifa" menyusi uchun end bilan.
8. Declarative mode: <Routes> va <Route>
Ko'p mavjud loyihalarda marshrutlar JSX ichida yozilgan. O'sha ikki sahifa declarative mode'da:
import { Link, MemoryRouter, Route, Routes } from "react-router";
function App() {
return (
<MemoryRouter initialEntries={["/"]}>
<nav>
<Link to="/">Bosh sahifa</Link>
{" | "}
<Link to="/menyu">Menyu</Link>
</nav>
<Routes>
<Route path="/" element={<h1>Xush kelibsiz</h1>} />
<Route path="/menyu" element={<h1>Menyu: osh, manti</h1>} />
</Routes>
</MemoryRouter>
);
}Ekranda:
Bosh sahifa | Menyu
Menyu: osh, manti<MemoryRouter> — createMemoryRouter ning komponent ko'rinishi, loyihada uning o'rnida <BrowserRouter>. <Routes> — ichidagi <Route> lardan joriy manzilga eng mosini tanlab chizadi. <Route path element> — bizning { path, element } obyektimizning JSX ko'rinishi.
| Declarative | Data | |
|---|---|---|
| Marshrutlar qayerda | JSX ichida, render paytida | obyektlar massivi, React'dan tashqarida |
| Router | <BrowserRouter> |
createBrowserRouter + <RouterProvider> |
Loader, action, errorElement |
yo'q | bor |
| Qayerda ko'rasiz | eski va oddiy loyihalar | yangi loyihalar, bu kurs |
Asosiy farq — birinchi qatorda. Data mode'da router barcha marshrutlarni oldindan biladi. Shu sababli sahifani chizishdan oldin uning ma'lumotini yuklashni boshlay oladi. Declarative mode'da marshrut faqat chizilganda ma'lum bo'ladi.
Maslahat: Eski kodda yana quyidagilarni ko'rasiz:
<Switch>(5-versiya, hozir<Routes>),component={Menu}vaexact(5-versiya; hozirelementva sukut bo'yicha aniq moslik),useHistory(hoziruseNavigate). Bu nomlarni ko'rsangiz — kod 2021-yildan oldin yozilgan.
9. Ko'p uchraydigan xatolar
9.1 Link router'dan tashqarida
import { Link } from "react-router";
function App() {
return <Link to="/menyu">Menyu</Link>;
}Konsolda:
Uncaught TypeError: Cannot destructure property 'basename' of 'React$111.useContext(...)' as it is null.Tarjimasi: "React$111.useContext(...) dan basename xususiyatini (property) ajratib bo'lmaydi, chunki u null." Xabar Link haqida hech narsa demaydi — shuning uchun chalg'itadi. Nega basename? Link birinchi qatorda router kontekstidan basename ni oladi (Destructuring). Router yo'q — kontekst null, null dan xususiyat olib bo'lmaydi. React$111 — kutubxonalar bitta faylga yig'ilganda React ga berilgan nom; o'z loyihangizda boshqacha bo'ladi (masalan React3). Qoida: basename, navigator, useContext(...) … null haqidagi xato — deyarli doim "router'dan tashqarida" degani.
Hooklar aniqroq gapiradi. useLocation() ni router'dan tashqarida chaqirsangiz:
Uncaught Error: useLocation() may be used only in the context of a <Router> component."useLocation() faqat <Router> komponenti kontekstida ishlatilishi mumkin." Davosi ikkalasida bir xil: komponent RouterProvider (yoki MemoryRouter/BrowserRouter) ichida chizilsin.
9.2 Boshqa xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
Vite: Failed to resolve import "react-router-dom" |
8-versiyada bu paket yo'q | "react-router" dan import |
| har bosishda oq ekran, Network'da HTML so'rovi | <a href> ilova ichida |
<Link to> |
| sahifa almashganda hamma narsa "unutiladi" | router komponent ichida yaratilgan | createBrowserRouter — modul darajasida |
ikki havolada aria-current |
end siz ota bo'lim havolasi |
kerakli joyda end |
| natija oynasida "Invalid URL" | createBrowserRouter jonli misolda |
createMemoryRouter |
| bola sahifa chiqmaydi, faqat layout | layout'da <Outlet /> yo'q |
<Outlet /> qo'shing |
10. Vazifalar qadami: uch sahifa (holat 02)
O'tgan darsda feat/sahifalar branch'ida server tomonini tayyorladik. Endi router.
10.1 Marshrutlar — src/marshrutlar.tsx
Kanondan, aynan:
// marshrutlar.tsx — sahifalar xaritasi (19/#02). React Router 8, "data
// mode": marshrutlar — obyektlar (path → element), router ularni
// createBrowserRouter bilan oladi (main.tsx). Layout marshrut (Qobiq) —
// umumiy qism, bolalari uning <Outlet /> o'rnida. errorElement — shu
// marshrut yoki bolalari xato tashlasa
import type { RouteObject } from "react-router";
import { Qobiq } from "./sahifalar/Qobiq.tsx";
import { RoyxatSahifasi } from "./sahifalar/RoyxatSahifasi.tsx";
import { SozlamalarSahifasi } from "./sahifalar/SozlamalarSahifasi.tsx";
import { StatistikaSahifasi } from "./sahifalar/StatistikaSahifasi.tsx";
import { TopilmadiSahifasi } from "./sahifalar/TopilmadiSahifasi.tsx";
import { XatoSahifasi } from "./sahifalar/XatoSahifasi.tsx";
export const marshrutlar: RouteObject[] = [
{
path: "/",
element: <Qobiq />,
errorElement: <XatoSahifasi />,
children: [
// index — ota manzilining o'zi ("/")
{ index: true, element: <RoyxatSahifasi /> },
{ path: "statistika", element: <StatistikaSahifasi /> },
{ path: "sozlamalar", element: <SozlamalarSahifasi /> },
// "*" — boshqa hech biriga mos kelmagan manzil
{ path: "*", element: <TopilmadiSahifasi /> },
],
},
];RouteObject — marshrut obyektining TypeScript turi (TypeScript qismidagi kabi kutubxona turlari). Ikki yangi narsa bir gap bilan: "*" — hech bir marshrutga mos kelmagan manzil (404 sahifa), errorElement — sahifa xato tashlasa, uning o'rnida chiziladigan sahifa. Ikkalasi ham to'liq — 3- va 5-darslarda.
Marshrutlar alohida faylda — "sahifalar xaritasi" bitta joyda ko'rinadi. sahifalar/ papkasida har sahifa — alohida komponent. RoyxatSahifasi — 18-qismdagi butun App ni o'raydi: ro'yxat sahifasi o'zgarmadi, faqat endi u bitta sahifa.
10.2 Router — src/main.tsx
// Router (19/#02): History API bilan (manzil — oddiy yo'l, # siz).
// basename — ilova papkasi (Pages'da "/vazifalar", vite.config.ts base):
// marshrutlarda "/statistika" yoziladi, manzilda /vazifalar/statistika.
// Marshrutlarda loader yo'q — birinchi chizish sinxron qoladi (CLS 0)
const router = createBrowserRouter(marshrutlar, {
basename: import.meta.env.BASE_URL.replace(/\/$/, "") || "/",
});basename — ilova joylashgan papka. O'tgan darsda Vite'ga base: "/vazifalar/" berdik — fayllar uchun. Router ham papkani bilishi kerak: manzil /vazifalar/statistika, marshrut esa "statistika". basename bilan router papkani o'zi qo'shadi va olib tashlaydi. Link to="/statistika" → href="/vazifalar/statistika". replace(/\/$/, "") — oxiridagi / ni olib tashlaydi ("/vazifalar/" → "/vazifalar"), Vercel'da esa "/" qoladi.
createRoot(...).render(...) ichida avvalgi <Ilova /> o'rniga endi <RouterProvider router={router} />. U hamon flushSync ichida: loader yo'q, demak router birinchi sahifani darhol, sinxron chizadi — sahifa ro'yxat bilan birga paydo bo'ladi (CLS 0, 18-qism).
10.3 Umumiy qism — sahifalar/Qobiq.tsx
Qobiq — layout marshrut. Unga 18-qismdagi skip link va foydalanuvchi menyusi App dan ko'chdi. Bo'limlar menyusi:
{/* NavLink — joriy sahifa havolasiga aria-current="page" qo'yadi
(ekran o'quvchi "joriy sahifa" deydi, CSS shu atributga
qaraydi). end — "/" faqat aynan ro'yxat sahifasida joriy */}
<nav className="bolimlar" aria-label="Bo'limlar">
<ul>
<li>
<NavLink to="/" end>
Ro'yxat
</NavLink>
</li>
<li>
<NavLink to="/statistika">Statistika</NavLink>
</li>
<li>
<NavLink to="/sozlamalar">Sozlamalar</NavLink>
</li>
</ul>
</nav>
<FoydalanuvchiMenyusi />
<Outlet context={kontekst} /><nav aria-label="Bo'limlar"> — Malika "Bo'limlar navigatsiyasi" ni eshitadi va unga bir tugma bilan o'ta oladi. <Outlet context={kontekst}> — layout sahifaga qiymat uzatadi (bu yerda — sarlavha ref'i, skip link uchun). U useOutletContext() bilan olinadi — React Router II da batafsil. CSS — «Uslub: atributga qarab» bo'limidagi qoida.
10.4 Natija
Testlar: node 174, Vitest 49 (App testlari router'siz ham ishlaydi). Hajm — «Bepul emas» bo'limidagi 120.1 KB. Yangi brauzer tekshiruvi e2e-19.mjs — 19 ta, hammasi dist/ da, CSP ostida (brauzer-tekshiruv/natija-19-02.txt dan):
✅ menyu: uch bo'lim, joriy — Ro'yxat (aria-current): ["Ro'yxat*","Statistika","Sozlamalar"]
✅ Statistika: sahifa qayta yuklanmadi (SPA), sarlavha va title: [true,"Statistika","Statistika — Vazifalar"]
✅ Orqaga: ro'yxat, filtr URL'da qoldi: ["/?filtr=faol",["1","3"],true]
✅ 404: noma'lum manzil — «Sahifa topilmadi», menyu bor: ["Sahifa topilmadi","«/yoq/sahifa» manzilida sahifa yo'q.",3]
✅ Pages: /vazifalar/statistika — HTTP 404 (404.html), lekin sahifa ochildi: [404,"Statistika"]
✅ Pages oflayn: /vazifalar/statistika — keshdan: "Statistika""Sahifa qayta yuklanmadi" qanday tekshirildi? Tekshiruvchi oynaga belgi qo'yadi (window.__belgi = 1), havolani bosadi va belgi joyidami — qaraydi. Sahifa qayta yuklanganda window yangidan yaratiladi va belgi yo'qoladi. Bu — SPA'ning eng oddiy isboti.
O'tgan darsdagi chuqur havola muammosi ham hal bo'ldi. Pages kabi serverda, /vazifalar/ papkasida (natijalar/02-chuqur-havola-v9.txt):
/vazifalar/vazifa/2 → HTTP 404 | #ilova: 19 element | h2: "Sahifa topilmadi" | title: "Sahifa topilmadi — Vazifalar"Fayllar topildi, ilova ochildi, router manzilni o'qidi. /vazifa/2 marshruti hali yo'q — shuning uchun "Sahifa topilmadi". Bu to'g'ri javob: keyingi darsda shu marshrutni qo'shamiz. 13–18-qismning 159 ta va 18-qismning 72 ta brauzer tekshiruvi ham o'tdi — ro'yxat sahifasining xulqi o'zgarmadi.
10.5 Commit
git add -A
git commit -m "feat: sahifalar — React Router 8 (data mode), layout, 404, Pages zaxirasi"Kanonda commit tanasida o'zgarishlar ro'yxati bor: marshrutlar.tsx, sahifalar/, base — BASE_PATH, 404.html (Vite plagini), %BASE_URL%, SW navigatsiyasi, skip link va foydalanuvchi menyusi Qobiq'da. Jami 21 fayl, +405 −72 qator.
11. Mashqlar
Mashqlar kurs/mashqlar/19/02-react-router/ da. Har yechimni «Ishga tushir» bilan sinang.
1-mashq (oson): bo'sh joylarni to'ldiring
- 8-versiyada o'rnatiladigan paket:
npm i Linkbrauzerda chizadigan teg:<>NavLinkjoriy havolaga qo'yadigan atribut:aria-current=""- Natija oynasida ishlaydigan router:
createRouter - Layout'da bola sahifa chiziladigan komponent:
</>
Yechim
react-router — 8-versiyada yagona paket (react-router-dom olib tashlangan). Link — oddiy <a>, shuning uchun yangi tabda ochish va nusxalash ishlaydi. aria-current="page" — ekran o'quvchi uchun "joriy sahifa". createMemoryRouter — tarix xotirada, manzil satri kerak emas. <Outlet /> — ota marshrutning "rozetkasi".
2-mashq (o'rta): «Aloqa» sahifasi
«aria-current va active» bo'limidagi misolga uchinchi sahifa qo'shing:
- manzil
/aloqa, sarlavhaTel: +998 90 000 00 00; - menyuda
NavLink— «Aloqa»; CurrentLinkham o'zgarishsiz ishlasin.
Sinov: «Aloqa» ni bosing — konsolda /aloqa → Aloqa active.
Yechim
import { useEffect } from "react";
import {
createMemoryRouter,
NavLink,
Outlet,
RouterProvider,
useLocation,
} from "react-router";
function CurrentLink() {
const { pathname } = useLocation();
useEffect(() => {
const link = document.querySelector('[aria-current="page"]');
console.log(pathname, "→", link.textContent, link.className);
}, [pathname]);
return null;
}
function Layout() {
return (
<div>
<nav aria-label="Bo'limlar">
<NavLink to="/" end>Bosh sahifa</NavLink>
{" | "}
<NavLink to="/menyu">Menyu</NavLink>
{" | "}
<NavLink to="/aloqa">Aloqa</NavLink>
</nav>
<CurrentLink />
<Outlet />
</div>
);
}
const router = createMemoryRouter([
{
path: "/",
element: <Layout />,
children: [
{ index: true, element: <h1>Xush kelibsiz</h1> },
{ path: "menyu", element: <h1>Menyu: osh, manti</h1> },
{ path: "aloqa", element: <h1>Tel: +998 90 000 00 00</h1> },
],
},
]);
export default function App() {
return <RouterProvider router={router} />;
}Konsolda:
/ → Bosh sahifa active
/aloqa → Aloqa activeEkranda:
Bosh sahifa | Menyu | Aloqa
Tel: +998 90 000 00 00Ikki joyga bitta qator: marshrut (children ga) va havola (menyuga). CurrentLink ga tegilmadi — u qaysi havola joriy ekanini DOM'dan oladi, NavLink esa atributni o'zi qo'yadi.
3-mashq (qiyin): declarative → data mode
Quyidagi ilova declarative mode'da yozilgan. Uni data mode'ga ko'chiring:
createMemoryRoutervaRouterProvider;- menyu — layout marshrutda, sahifalar —
<Outlet />da; - havolalar —
NavLink; - har noma'lum manzilga
Sahifa topilmadi(path: "*").
Sinov uchun router initialEntries: ["/eski-menyu"] bilan boshlansin (eskirgan havola), keyin mehmon «Bosh sahifa» ni bosadi.
import { Link, MemoryRouter, Route, Routes } from "react-router";
function App() {
return (
<MemoryRouter initialEntries={["/"]}>
<nav>
<Link to="/">Bosh sahifa</Link>
{" | "}
<Link to="/menyu">Menyu</Link>
</nav>
<Routes>
<Route path="/" element={<h1>Xush kelibsiz</h1>} />
<Route path="/menyu" element={<h1>Menyu: osh, manti</h1>} />
</Routes>
</MemoryRouter>
);
}Ekranda:
Bosh sahifa | Menyu
Xush kelibsizYechim
import {
createMemoryRouter,
NavLink,
Outlet,
RouterProvider,
} from "react-router";
function Layout() {
return (
<div>
<nav>
<NavLink to="/" end>Bosh sahifa</NavLink>
{" | "}
<NavLink to="/menyu">Menyu</NavLink>
</nav>
<Outlet />
</div>
);
}
const router = createMemoryRouter(
[
{
path: "/",
element: <Layout />,
children: [
{ index: true, element: <h1>Xush kelibsiz</h1> },
{ path: "menyu", element: <h1>Menyu: osh, manti</h1> },
{ path: "*", element: <h1>Sahifa topilmadi</h1> },
],
},
],
{ initialEntries: ["/eski-menyu"] },
);
export default function App() {
return <RouterProvider router={router} />;
}Ekranda:
Bosh sahifa | Menyu
Xush kelibsiz/eski-menyu hech bir marshrutga mos kelmadi — "*" ishladi, lekin menyu (layout) joyida qoldi: "*" ham Layout ning bolasi. Mehmon adashmaydi, menyudan kerakli sahifani tanlaydi. "*" ni children dan tashqariga, eng yuqori darajaga qo'ysangiz, 404 sahifa menyusiz chiqardi. vazifalar da TopilmadiSahifasi ham Qobiq ichida — xuddi shu sabab bilan.
4-mashq: vazifalar ga nazar
feat/sahifalar branch'ida «Vazifalar qadami» ni bajaring: npm i --save-exact react-router@8.4.0, marshrutlar.tsx, sahifalar/ (Qobiq, RoyxatSahifasi, StatistikaSahifasi, SozlamalarSahifasi, TopilmadiSahifasi, XatoSahifasi), main.tsx, CSS. npm run check toza bo'lsin. Keyin npm run dev da:
- «Statistika» ni bosing va DevTools → Network'da yangi HTML so'rovi bormi — qarang.
- Statistika sahifasida F5 bosing.
- Manzil satriga
/yoqyozing.
Yechim
- Network'da hujjat (Doc) so'rovi yo'q — faqat sahifa qismi almashdi. Elements panelida «Statistika» havolasida
aria-current="page". - F5 dan keyin ham statistika: Vite dev serveri SPA zaxirasini o'zi beradi.
- «Sahifa topilmadi» va ««/yoq» manzilida sahifa yo'q.» — menyu bilan.
npm run check— node 174, Vitest 49 test, lint, format, tip — toza. Agarformat:checkApp.tsx ga shikoyat qilsa —npm run format(kanonda ham bir marta shunday bo'ldi).
12. Real ishda
- Har React SPA'da: admin panel, shaxsiy kabinet, ichki tizimlar — React Router (yoki TanStack Router). Ish e'lonlarida "React Router" talablar ro'yxatida tez-tez uchraydi.
- Versiya migratsiyasi: real loyihalarda 5, 6, 7-versiyalar aralash yashaydi.
Switch,useHistory,react-router-domni ko'rib, versiyani aniqlash va ko'chirish — oddiy topshiriq. Rasmiy "Upgrading" qo'llanmasi bor. - Framework mode va Next.js: yangi loyihalarning bir qismi to'g'ridan-to'g'ri framework'dan boshlanadi. Data mode tushunchalari (marshrut, layout, loader) u yerda ham — framework mode va Next.js.
- Intervyu savollari: "
Linkva<a>farqi?", "NavLinknima qo'shadi?", "Declarative va data mode farqi?", "basenamenima uchun?", "Nega router komponentdan tashqarida yaratiladi?".
Xulosa
- React Router 8.4.0: bitta paket
react-router(react-router-domyo'q), narxi +29.4 KB gzip. - Data mode: marshrutlar —
{ path, element, children }obyektlari,createBrowserRouter+<RouterProvider>; natija oynasida —createMemoryRouter. Link—<a href>, sahifa qayta yuklanmaydi; sahifa almashganda eski sahifa unmount bo'ladi, layout joyida qoladi.NavLinkjoriy havolagaaria-current="page"vaactiveqo'yadi; CSS — atributga;end— faqat aniq manzilda.- Declarative mode (
<Routes>,<Route>) — eski va oddiy loyihalarda; loader va action faqat data mode'da. vazifalar: Qobiq + uch sahifa + 404,basename— Pages papkasi; 159 + 72 + 19 brauzer tekshiruvi o'tdi.
Keyingi dars: React Router II: dinamik, ichma-ich va layout marshrutlar — /vazifa/:id, useParams, ichma-ich <Outlet> va index marshrutlar.
Manbalar
- React Router: "Picking a Mode", "Data Mode — Installation, Routing, Navigating" — reactrouter.com (paket ichida:
node_modules/react-router/docs/start/) - React Router: CHANGELOG, v8.0.0 ("Remove
react-router-dompackage") va v8.4.0 — github.com/remix-run/react-router - React Router API:
createBrowserRouter,createMemoryRouter,RouterProvider,Link,NavLink— api.reactrouter.com - WAI-ARIA 1.2:
aria-current— w3.org/TR/wai-aria-1.2 - MDN: "aria-current" — developer.mozilla.org
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!