Mundarija (37)
- Bu darsda
- 1. Nega bu kerak?
- 2. <Link> — sahifalar orasida o'tish
- 2.1 href: satr yoki obyekt
- 2.2 replace, scroll, prefetch
- 2.3 Prefetch: biz o'lchadik
- 3. usePathname — joriy manzil
- 3.1 Kanondagi NavHavola
- 3.2 Natija oynasida
- 4. useRouter — kodda o'tish
- 4.1 Metodlar
- 4.2 OrqagaTugmasi
- 4.3 Natija oynasida: tarix
- 5. useSearchParams — URL'dagi filtr
- 5.1 Faqat o'qish uchun
- 5.2 Kanondagi TegFiltri
- 5.3 Natija oynasida: filtr modeli
- 5.4 Biz topdik: <Suspense> siz build to'xtaydi
- 6. Boshqa hook'lar
- 6.1 useParams
- 6.2 useSelectedLayoutSegment
- 6.3 useLinkStatus
- 7. redirect va permanentRedirect — serverda yo'naltirish
- 7.1 Ikki status
- 7.2 Natija oynasida va tuzoqlar
- 8. Portfolio qadami: navigatsiya va commit
- 8.1 Fayllar
- 8.2 Tekshiruv va commit
- 9. Ko'p uchraydigan xatolar
- 10. Mashqlar
- 1-mashq (oson): qaysi vosita?
- 2-mashq (o'rta): sahifalash
- 3-mashq (qiyin): boshqa parametrlarni saqlab qolish
- 4-mashq: Portfolio qadami
- 11. Real ishda
- Xulosa
- Manbalar
Next.js navigatsiya: Link, useRouter, usePathname va useSearchParams
Qisqacha: Next.js'da sahifalar orasida
next/linkdagi<Link href>bilan o'tiladi: u oddiy<a>chizadi, lekin sahifani qayta yuklamaydi va havola ekranda ko'ringanda manzilni oldindan yuklab (prefetch) qo'yadi. Kodda o'tish kerak bo'lsa —useRouter()(push,replace,back,refresh). Joriy manzilniusePathname(), query qismini (?teg=React)useSearchParams()o'qiydi — bular faqat client komponentda ishlaydi. Serverda esaredirect()307,permanentRedirect()308 statusi bilan boshqa manzilga yuboradi.
Bu darsda
<Link>ninghref,replace,scroll,prefetchimkoniyatlarini va prefetch narxini o'lchangan raqam bilan bilasiz.usePathnamebilan menyuda joriy sahifani belgilaysiz.useRouterbilan «Orqaga» tugmasi va dasturiy o'tish yozasiz.- Teg filtrini URL'ga ko'chirasiz (
?teg=React) va negauseSearchParams<Suspense>talab qilishini haqiqiy build xatosi bilan ko'rasiz. redirectvapermanentRedirectfarqini statuslar bilan tushuntirasiz.
Oldin bilishingiz kerak: Maxsus fayllar, Fayl-asosli routing, React Router III: dasturiy navigatsiya, URL — holat saqlash joyi.
1. Nega bu kerak?
v1 portfolio'da ikkita kichik JavaScript funksiya bor edi. Birinchisi — joriyHavolaniBelgila (Atributlar va dataset darsida): menyuda hozirgi sahifaning havolasiga aria-current="page" qo'yardi. Ikkinchisi — loyihalar teg filtri (Ko'p elementni samarali chizish): «React» bosilsa, faqat React loyihalari qolardi.
Filtrning bitta kamchiligi bor edi: tanlov xotirada yashardi. Do'stingizga "mening React loyihalarimni ko'r" deb havola yuborsangiz, u hamma loyihani ko'rardi. Sahifani yangilasangiz ham filtr yo'qolardi.
URL — holat saqlash joyi darsida bu muammoning yechimini bilasiz: filtr URL'da bo'lsin — /loyihalar?teg=React. Havolani ulashsa ham, yangilasa ham — tanlov joyida. React Router'da buni useSearchParams va <Link> bilan qilgandingiz.
Next.js'da g'oyalar o'sha, nomlar biroz boshqa. Bu darsda portfolio ga uchta kichik komponent qo'shamiz: joriy havola (NavHavola), «Orqaga» tugmasi (OrqagaTugmasi) va URL'dagi teg filtri (TegFiltri).
Hayotiy o'xshatish: taksi ilovasi. Manzilni yozasiz — ilova yo'lni oldindan hisoblab qo'yadi (prefetch). «Buyurtma» bosilganda mashina darhol yo'lga chiqadi. Manzil (URL) — buyurtmaning o'zida, telefonni o'chirib yoqsangiz ham yo'qolmaydi.
2. <Link> — sahifalar orasida o'tish
2.1 href: satr yoki obyekt
<Link> ni Fayl-asosli routing darsidan bilasiz. href ikki ko'rinishda bo'ladi:
import Link from "next/link";
<Link href="/loyihalar?teg=React">React loyihalari</Link>
<Link href={{ pathname: "/loyihalar", query: { teg: "React" } }}>
React loyihalari
</Link>Ikkalasi ham bir xil <a href="/loyihalar?teg=React"> chizadi. Obyekt shakli query'da maxsus belgi (bo'sh joy, &) bo'lganda qulay: kodlashni Next.js o'zi qiladi.
Natija oynasida tekshiramiz. Eslatma: u yerdagi next/link — o'quv modeli, haqiqiy Next.js'dagi server va prefetch u yerda yo'q.
import Link from "next/link";
import { usePathname, useSearchParams } from "next/navigation";
const TAG = "Node va Express";
export default function App() {
const pathname = usePathname();
const tag = useSearchParams().get("teg");
return (
<>
<Link href={{ pathname: "/loyihalar", query: { teg: TAG } }}>
Node va Express
</Link>
<p>Manzil: {pathname}</p>
<p>teg: {tag ?? "yo'q"}</p>
</>
);
}Ekranda:
Node va Express
Manzil: /loyihalar
teg: Node va ExpressHavolaning haqiqiy href atributi — /loyihalar?teg=Node+va+Express: bo'sh joy + ga kodlandi. get("teg") esa uni qaytarib ochib berdi. Kodlashni qo'lda qilsangiz, encodeURIComponent kerak bo'lardi — kanondagi TegFiltri aynan shunday qiladi.
2.2 replace, scroll, prefetch
| Prop | Sukut | Nima qiladi |
|---|---|---|
replace |
false |
Tarixga yangi qadam qo'shmaydi, joriysini almashtiradi |
scroll |
true |
O'tgach sahifa boshi ko'rinmasa — tepaga aylantiradi |
prefetch |
"auto" |
Havola ekranda ko'ringanda manzilni oldindan yuklaydi |
onNavigate |
— | O'tishdan oldin chaqiriladi, e.preventDefault() to'xtatadi |
replace ni React Router III darsidan bilasiz: filtr har bosishda tarixga yangi qadam qo'shsa, «Orqaga» tugmasi foydalanuvchini har bir tegdan qaytarib chiqaradi. Ba'zan shuni xohlaysiz, ba'zan yo'q.
scroll ga Next.js hujjatidagi qoida: yangi sahifa ekranda ko'rinib turgan bo'lsa, joy saqlanadi; ko'rinmasa — sahifa boshiga aylantiriladi. scroll={false} bu aylantirishni o'chiradi. Teg filtri uchun aynan kerak: foydalanuvchi ro'yxatning o'rtasida «React» ni bosdi — sahifa tepaga sakramasligi kerak.
2.3 Prefetch: biz o'lchadik
Prefetch — <Link> ning eng katta afzalligi va eng katta narxi. Havola ekranga chiqishi bilan Next.js shu sahifa uchun ma'lumotni (RSC payload — Server Components darsida ochamiz) fonda so'rab qo'yadi. Bosilganda tarmoqni kutmaydi.
Vaqt bo'yicha:
sequenceDiagram
participant B as Brauzer
participant S as Server
Note over B: Sahifa ochildi, havolalar ko'rindi
B->>S: RSC: /loyihalar (prefetch)
B->>S: RSC: /aloqa (prefetch)
S-->>B: payload'lar xotiraga
Note over B: Foydalanuvchi «Loyihalar» ni bosdi
Note over B: Tarmoqsiz — darhol yangi sahifaPrefetch faqat production'da ishlaydi (next dev da yo'q). Biz portfolio ning holat 06 nusxasini next build + next start bilan ishga tushirdik. Headless Chrome'da bosh sahifani ochib, RSC: 1 sarlavhali so'rovlarni sanadik, keyin menyudagi «Loyihalar» ni bosdik:
yuklash: / (prefetch) ×1
yuklash: /haqimda (prefetch) ×2
yuklash: /loyihalar (prefetch) ×2
yuklash: /aloqa (prefetch) ×2
yuklash: /loyihalar/vazifalar (prefetch) ×2
yuklash: /loyihalar/bahor (prefetch) ×1
manzil: /loyihalar | h1: Loyihalar | to'liq yuklash: 1 1O'qiymiz:
- Sahifa ochilishi bilan 10 ta prefetch so'rovi ketdi — ekrandagi har havola uchun (menyu va bosh sahifadagi loyiha kartalari). Ba'zisi ikki marta: Next 16.4 da Partial Prefetching yoqilgan — avval sahifaning statik qobig'i, keyin qolgani.
- «Loyihalar» bosilgandan keyin yangi so'rov yo'q — hammasi oldindan olingan edi. Ro'yxatda "bosilgach" qatori umuman chiqmadi.
to'liq yuklash: 1— brauzer sahifani bir marta yuklagan, o'tish qayta yuklashsiz.
Narxi: foydalanuvchi bosmagan sahifalar ham yuklandi. Kanon Performance bosqichida buni yakuniy holatda o'lchadi: sahifa ochilganda 13–22 ta RSC so'rov, 56–61 KB (xom). Kanon prefetch'ni o'chirmadi — portfolio kichik, o'tish bir zumda. Yuzlab havolali sahifada (katalog, jadval) esa prefetch={false} o'ylab ko'rishga arziydi.
Tekshirib ko'ring:
next devda Network panelini ochib menyudan o'tsangiz, prefetch so'rovlarini ko'rasizmi?
Javob
Yo'q. Prefetch faqat production build'da (next build + next start) ishlaydi. Dev'da sahifa bosilganda so'raladi. Shuning uchun prefetch'ni o'lchash yoki tekshirish — doim prod rejimda.
3. usePathname — joriy manzil
3.1 Kanondagi NavHavola
Layout va template darsida va'da bergan edik: layout joriy manzilni bilmaydi, uni kichik client komponent o'qiydi. Mana u:
"use client";
import Link from "next/link";
import { usePathname } from "next/navigation";
// Joriy sahifani belgilash (v1 W-05 dagi joriyHavolaniBelgila)
// — endi usePathname bilan (N-06).
export function NavHavola({
href,
children,
}: {
href: string;
children: React.ReactNode;
}) {
const yol = usePathname();
const joriy =
href === "/" ? yol === "/" : yol === href || yol.startsWith(`${href}/`);
return (
<Link href={href} aria-current={joriy ? "page" : undefined}>
{children}
</Link>
);
}Uchta narsa:
"use client"—usePathnamehook, hook'lar esa faqat brauzerda ham ishlaydigan komponentda. Nega aynan shunday — Client Components darsida.joriyqoidasi./loyihalar/vazifalarda ham «Loyihalar» joriy bo'lishi kerak — shuning uchunstartsWith(${href}/). Lekin/hamma manzilning boshi: uni alohida, faqat aniq tenglik bilan tekshiramiz./qo'shilgani bejiz emas.startsWith(href)yozilsa,/loyihalar-arxivham «Loyihalar» ni yoqib yuborardi.
SaytSarlavhasi (server komponent) menyuni MENYU massividan chizadi va har bandni NavHavola ga beradi. Layout serverda qoladi — faqat to'rtta kichik havola brauzerda jonlanadi.
3.2 Natija oynasida
Kanondagi qoida jonli. Model ih.boshla bilan /loyihalar/vazifalar dan boshlaydi (bu — o'quv modeli sozlamasi, Next.js API'si emas):
import Link from "next/link";
import { ih, usePathname } from "next/navigation";
import type { ReactNode } from "react";
ih.boshla("/loyihalar/vazifalar");
type Props = { href: string; children: ReactNode };
function NavLink({ href, children }: Props) {
const path = usePathname();
const current =
href === "/"
? path === "/"
: path === href || path.startsWith(`${href}/`);
console.log(`${href}: ${current ? "joriy" : "-"}`);
return (
<Link href={href} aria-current={current ? "page" : undefined}>
{current ? `[${children}]` : children}
</Link>
);
}
export default function App() {
return (
<nav>
<NavLink href="/">Bosh sahifa</NavLink>{" "}
<NavLink href="/loyihalar">Loyihalar</NavLink>
</nav>
);
}Konsolda:
/: -
/loyihalar: joriy
/: joriy
/loyihalar: -Ekranda:
[Bosh sahifa] LoyihalarAvval /loyihalar/vazifalar da «Loyihalar» joriy edi — ichki sahifa bo'lsa ham. «Bosh sahifa» bosilgach belgi ko'chdi. Kvadrat qavs — faqat natija oynasida ko'rinsin deb; kanonda belgi CSS'da ([aria-current="page"] selektori).
4. useRouter — kodda o'tish
4.1 Metodlar
useRouter() — next/navigation dagi hook. React Router dagi useNavigate ning o'rni, faqat metodlari alohida:
| Metod | Nima qiladi |
|---|---|
push(href) |
Yangi manzilga o'tadi, tarixga qo'shadi |
replace(href) |
O'tadi, joriy tarix qadamini almashtiradi |
back() / forward() |
Brauzer tarixida orqaga / oldinga |
refresh() |
Server komponentlarni qayta so'raydi, client state saqlanadi |
prefetch(href) |
Manzilni qo'lda oldindan yuklaydi |
Diqqat:
next/routeremas,next/navigation.next/router— eski Pages Router'niki. App Router'da undan import qilsangiz, Next.js "NextRouter was not mounted" xatosini beradi (Pages Router merosi darsida). Eski maqolalarda ko'p uchraydi.
refresh() ning g'alati tomoni: u sahifani qayta yuklamaydi. Serverdan yangi ma'lumot oladi va faqat server qismlarini yangilaydi; inputga yozilgan matn, ochiq menyu saqlanadi. Buni Cache Components II darsida ishlatamiz.
Qachon <Link>, qachon router.push? Qoida oddiy: foydalanuvchi bosadigan havola bo'lsa — <Link>. U <a> chizadi: yangi tabda ochish, havolani nusxalash, ekran o'quvchi — hammasi ishlaydi. router.push — kod natijasida o'tish uchun: forma saqlangandan keyin, taymer tugaganda.
4.2 OrqagaTugmasi
Kanondagi komponent — 404 sahifasidagi «Orqaga qaytish»:
"use client";
import { useRouter } from "next/navigation";
// useRouter — dasturiy navigatsiya (N-06): tarixda bir qadam
// orqaga.
export function OrqagaTugmasi() {
const router = useRouter();
return (
<button type="button" onClick={() => router.back()}>
Orqaga qaytish
</button>
);
}Bu yerda <Link> emas, <button>: "orqaga" — bitta manzil emas, foydalanuvchining shaxsiy tarixi. Kanon oqim tekshiruvi (/loyihalar dan noto'g'ri havolaga o'tib, 404 da «Orqaga qaytish»):
Orqaga: /loyihalarFoydalanuvchi qaerdan kelgan bo'lsa, o'sha yerga qaytdi.
4.3 Natija oynasida: tarix
push, replace va back ning farqi — tarixda. Ketma-ket bosing:
import { usePathname, useRouter } from "next/navigation";
export default function App() {
const router = useRouter();
const pathname = usePathname();
console.log("manzil:", pathname);
return (
<>
<button onClick={() => router.push("/a")}>push /a</button>{" "}
<button onClick={() => router.replace("/b")}>
replace /b
</button>{" "}
<button onClick={() => router.back()}>back</button>
<p>Manzil: {pathname}</p>
</>
);
}Konsolda:
manzil: /
manzil: /a
manzil: /b
manzil: /Ekranda:
push /a replace /b back
Manzil: /Tarix qadamma-qadam:
| Qadam | Tarix | Joriy |
|---|---|---|
| boshlanish | / |
/ |
push("/a") |
/, /a |
/a |
replace("/b") |
/, /b |
/b |
back() |
/, /b |
/ |
replace /a ni /b bilan almashtirdi — shuning uchun back() /a ga emas, to'g'ridan / ga qaytdi.
5. useSearchParams — URL'dagi filtr
5.1 Faqat o'qish uchun
useSearchParams() — ?teg=React qismini URLSearchParams ko'rinishida qaytaradi: .get("teg"), .has(), .getAll() ishlaydi. Lekin u faqat o'qish uchun: .set() chaqirsangiz, Next.js xato beradi.
import { useSearchParams } from "next/navigation";
export default function App() {
const params = useSearchParams();
function tryChange() {
try {
params.set("teg", "React");
} catch (error) {
console.log((error as Error).message);
}
}
return <button onClick={tryChange}>Sinash</button>;
}Konsolda:
Method unavailable on `ReadonlyURLSearchParams`. Read more: https://nextjs.org/docs/app/api-reference/functions/use-search-params#updating-searchparamsTarjimasi: "ReadonlyURLSearchParams da bu metod yo'q". Model bu xabarni Next.js'dagi matn bilan beradi. Sabab: URL o'zgarmasa, React sahifani qayta chizmaydi. Query'ni o'zgartirish yo'li bitta — yangi manzilga o'tish: <Link href="?teg=React"> yoki router.push(...). Yangi qiymatlar to'plamini yasash kerak bo'lsa — nusxa oling: new URLSearchParams(params.toString()), o'zgartiring, keyin o'ting.
5.2 Kanondagi TegFiltri
"use client";
import Link from "next/link";
import { usePathname, useSearchParams } from "next/navigation";
type Element = { kalit: string; teglar: string[]; tugun: React.ReactNode };
// v1 dagi teglar filtri (W-09/W-11) — endi URL'da (?teg=React).
export function TegFiltri({ elementlar }: { elementlar: Element[] }) {
const teg = useSearchParams().get("teg");
const yol = usePathname();
const hammaTeglar = [...new Set(elementlar.flatMap((e) => e.teglar))].sort();
const korinadi = teg
? elementlar.filter((e) => e.teglar.includes(teg))
: elementlar;
return (
<>
<nav aria-label="Texnologiya bo'yicha filtr">
<ul className="teglar">
<li>
<Link
href={yol}
aria-current={teg ? undefined : "page"}
scroll={false}
>
Hammasi
</Link>
</li>
{hammaTeglar.map((t) => (
<li key={t}>
<Link
href={`${yol}?teg=${encodeURIComponent(t)}`}
aria-current={t === teg ? "page" : undefined}
scroll={false}
>
{t}
</Link>
</li>
))}
</ul>
</nav>
<p role="status">{korinadi.length} ta loyiha ko'rsatilmoqda</p>
<ul className="loyihalar">
{korinadi.map((e) => (
<li key={e.kalit}>{e.tugun}</li>
))}
</ul>
</>
);
}Kanon izohi qisqartirildi (ikkinchi yarmi — tugun haqida — Kompozitsiya darsida). Ikki qator kanonda 70 belgidan uzun, aynan qoldirildi.
Qatorma-qator:
| Qism | Nima qiladi |
|---|---|
useSearchParams().get("teg") |
?teg= qiymati yoki null |
new Set(…flatMap…) |
Hamma loyihalardagi teglar, takrorsiz |
filter |
Tanlangan tegli loyihalar |
scroll={false} |
Bosilganda sahifa tepaga sakramaydi |
role="status" |
Ekran o'quvchi "1 ta loyiha" ni o'qiydi |
Filtr tugmalari — havolalar, onClick li tugmalar emas. Har teg — haqiqiy manzil: yangi tabda ochish, nusxalash, ulashish ishlaydi. v1 dagi filtrning kamchiligi shu bilan yopildi.
5.3 Natija oynasida: filtr modeli
Kanon komponentining soddalashtirilgan nusxasi (tugun o'rniga oddiy nom):
import Link from "next/link";
import { usePathname, useSearchParams } from "next/navigation";
const PROJECTS = [
{ slug: "vazifalar", tags: ["React", "TypeScript"] },
{ slug: "bahor", tags: ["HTML", "CSS"] },
{ slug: "landing", tags: ["HTML"] },
];
export default function App() {
const tag = useSearchParams().get("teg");
const path = usePathname();
const allTags = [...new Set(PROJECTS.flatMap((p) => p.tags))];
allTags.sort();
const visible = tag
? PROJECTS.filter((p) => p.tags.includes(tag))
: PROJECTS;
return (
<>
<nav>
<Link href={path} scroll={false}>Hammasi</Link>
{allTags.map((t) => (
<span key={t}>
{" "}
<Link
href={`${path}?teg=${encodeURIComponent(t)}`}
scroll={false}
>
{t === tag ? `[${t}]` : t}
</Link>
</span>
))}
</nav>
<p role="status">{visible.length} ta loyiha ko'rsatilmoqda</p>
<ul>
{visible.map((p) => (
<li key={p.slug}>{p.slug}</li>
))}
</ul>
</>
);
}Ekranda:
Hammasi CSS HTML [React] TypeScript
1 ta loyiha ko'rsatilmoqda
vazifalarAvval «HTML» (2 ta), keyin «React» (1 ta). Kanonning haqiqiy brauzer tekshiruvi ham xuddi shu raqamlarni bergan:
?teg=React: 1 ta loyiha ko'rsatilmoqda | joriy: [ 'Loyihalar', 'React' ]
HTML bosildi: /loyihalar?teg=HTML 2 ta loyiha ko'rsatilmoqda
Hammasi: 3 ta loyiha ko'rsatilmoqda
nav joriy: Loyihalarjoriy: [ 'Loyihalar', 'React' ] — sahifada ikkita aria-current="page": menyuda «Loyihalar», filtrda «React». Ikkalasi ham to'g'ri — har biri o'z nav ichida.
5.4 Biz topdik: <Suspense> siz build to'xtaydi
useSearchParams qiymati build vaqtida noma'lum: /loyihalar statik (○) sahifa, uni bitta HTML qilib tayyorlash kerak, ?teg= esa faqat so'rov kelganda ma'lum bo'ladi. Kanon TegFiltri ni <Suspense> ga o'ragan. Biz o'ramni olib tashlab build qildik:
Error: Route "/loyihalar": Next.js encountered URL data `useSearchParams()` in a Client Component outside of `<Suspense>`.
This blocks prerendering because the value is only available at runtime.
Ways to fix this:
- [stream] Wrap the component in `<Suspense fallback={...}>` so the hook value streams in after prerendering
- [block] Set `export const instant = false` to allow a blocking routeTarjimasi: "/loyihalar marshruti: Next.js <Suspense> dan tashqaridagi Client Component'da URL ma'lumotiga (useSearchParams()) duch keldi. Qiymat faqat ish vaqtida ma'lum, shuning uchun oldindan chizish to'xtaydi." Yechimlar: [stream] — komponentni <Suspense> ga o'rang; [block] — sahifani "bloklovchi" qiling (ya'ni har so'rovda to'liq chizilsin). Cache Components o'chiq bo'lgan eski loyihalarda bu xato bo'lmaydi — u yerda Next.js sahifani jim "client'da chiziladigan" qiladi.
Kanondagi yechim — loyihalar/page.tsx da:
{/* useSearchParams prerender'da noma'lum — Suspense'gacha
client'da chiziladi; zaxira — filtrsiz to'liq ro'yxat */}
<Suspense fallback={<ul className="loyihalar">{kartalar}</ul>}>
<TegFiltri elementlar={…} />
</Suspense>fallback — oddiy "Yuklanmoqda…" emas, to'liq ro'yxat. Statik HTML'da hamma uchta karta bor: JS o'chiq brauzer va qidiruv boti ham loyihalarni ko'radi. JS yuklangach TegFiltri o'z joyini egallaydi va ?teg= ni hisobga oladi. Natija: /loyihalar ○ statik qoldi.
Tekshirib ko'ring: sahifa (
page.tsx) hamsearchParamsprop oladi.TegFiltrio'rniga sahifaning o'zidaawait searchParamsqilsak nima o'zgaradi?
Javob
Sahifa statik bo'lmay qoladi: query faqat so'rovda ma'lum, demak sahifa har so'rovda serverda chiziladi (yoki uni ham Suspense ichiga olish kerak). Kanon filtrni brauzerga berdi — sahifa ○ qoldi, filtr esa bir zumda ishlaydi. Statik va dinamik farqini Rendering strategiyalari darsida o'lchaymiz.
6. Boshqa hook'lar
6.1 useParams
Dinamik segment qiymati — client komponentda. Fayl-asosli routing darsida ko'rgansiz: useParams<{ slug: string }>() → { slug: "vazifalar" }. Server sahifadagi params dan farqi: Promise emas, qiymat darhol.
6.2 useSelectedLayoutSegment
Layout ichidagi client komponent "mendan pastda qaysi segment ochiq?" deb so'rashi mumkin: app/loyihalar/layout.tsx ichida /loyihalar/vazifalar ochiq bo'lsa — "vazifalar". useSelectedLayoutSegments() (ko'plik) — pastdagi hamma segmentlar massivi. Bo'lim ichidagi tablar uchun qulay. Natija oynasidagi modelda bu hook'lar ildizga nisbatan ishlaydi (haqiqiy Next.js'da — layout'ga nisbatan), shuning uchun jonli misol bermaymiz.
6.3 useLinkStatus
<Link> ichidagi komponent useLinkStatus() dan { pending } oladi: o'tish kutilayotganda true. Sekin tarmoqda havola yonida kichik indikator ko'rsatish uchun. Prefetch qilingan sahifada u deyarli doim false — o'tish darhol. Natija oynasidagi modelda o'tish sinxron, u yerda doim { pending: false }.
7. redirect va permanentRedirect — serverda yo'naltirish
7.1 Ikki status
Ba'zan foydalanuvchini sahifa chizilmasdan boshqa manzilga yuborish kerak: eski URL, kirmagan foydalanuvchi. Server komponentda buning uchun next/navigation dan ikki funksiya:
import { redirect } from "next/navigation";
export default function EskiYol() {
redirect("/loyihalar");
}Biz ikkala variantni tajriba sahifalariga qo'yib, curl -D - bilan so'radik:
== /eski-yol
HTTP/1.1 307 Temporary Redirect
location: /loyihalar
== /eski-doimiy
HTTP/1.1 308 Permanent Redirect
location: /loyihalar| Funksiya | Status | Qachon |
|---|---|---|
redirect() |
307 — vaqtinchalik | Kirish talab qilinadi, vaqtinchalik ko'chish |
permanentRedirect() |
308 — doimiy | URL abadiy o'zgardi (slug yangilandi) |
Google uchun farq katta: 308 da u eski manzilni indeksdan olib, "kuchini" yangisiga o'tkazadi; 307 da eski manzilni saqlaydi. Holat kodlari HTTP metodlari va status kodlari darslarida chuqur keladi.
Ikkala sahifa build jadvalida ○ — statik. Yo'naltirish ham build vaqtida tayyorlanib qo'yildi (x-nextjs-cache: HIT).
7.2 Natija oynasida va tuzoqlar
redirect() ham notFound() kabi xato tashlaydi — NEXT_REDIRECT. Modelda hodisa ichida sinab ko'ramiz (render ichida emas: modelda render paytidagi redirect() ni ushlab, manzilni almashtiradigan qism yo'q — natija oynasida u ushlanmagan xato bo'ladi; haqiqiy Next.js'da esa uni router ushlaydi):
import { redirect, usePathname } from "next/navigation";
export default function App() {
const pathname = usePathname();
function goLogin() {
try {
redirect("/kirish");
} catch (error) {
const e = error as Error & { digest?: string };
console.log(e.message, "|", e.digest);
}
}
return (
<>
<button onClick={goLogin}>Kirish</button>
<p>Manzil: {pathname}</p>
</>
);
}Konsolda:
NEXT_REDIRECT | NEXT_REDIRECT;replace;/kirish;307;Ekranda:
Kirish
Manzil: /kirishdigest ichida hamma ma'lumot: tur (replace), manzil, status (307). Next.js aynan shu xatoni ushlab, javobni 307 ga aylantiradi.
Ikki qoida:
try/catchichida chaqirmang (notFound()dagidek). Modelda biz ataylab ushladik — ichini ko'rish uchun. Haqiqiy koddacatchyo'naltirishni yutib yuboradi.- Client komponentda hodisa ichida
redirect()emas,router.push()ishlating.redirect()— render paytida (server komponent, Server Action) ishlatish uchun.
8. Portfolio qadami: navigatsiya va commit
8.1 Fayllar
Maxsus fayllar darsidagi fayllarga qo'shiladi:
| Fayl | Nima |
|---|---|
komponentlar/NavHavola.tsx |
Client: usePathname → aria-current="page" |
komponentlar/SaytSarlavhasi.tsx |
Menyu <Link> o'rniga NavHavola |
komponentlar/OrqagaTugmasi.tsx |
Client: router.back(), 404 sahifasida |
komponentlar/TegFiltri.tsx |
Client: ?teg=, scroll={false}, role="status" |
app/loyihalar/page.tsx |
TegFiltri <Suspense> ichida, zaxira — to'liq ro'yxat |
8.2 Tekshiruv va commit
Build jadvali o'zgarmadi: hamma sahifa ○, ◐ /loyihalar/[slug]. Kanon o'lchovi:
| Holat 04 | Holat 06 | |
|---|---|---|
| JS (gzip) | 135.4 KB, 6 fayl | 137.4 KB, 9 fayl |
| axe | /yoq da 3 |
0 |
| hydration | 0 | 0 |
+2.0 KB — uchta client komponent (NavHavola, OrqagaTugmasi, TegFiltri). Server komponentlar (sahifalar, kartalar) brauzerga JS qo'shmadi. Lint, tip, format — 0.
git add -A
git commitCommit (kanon): Navigatsiya: not-found, error, global-error, loading; joriy havola, teg filtri URL'da, Orqaga tugmasi.
9. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
NextRouter was not mounted |
next/router dan import |
next/navigation |
Method unavailable on ReadonlyURLSearchParams |
useSearchParams().set() |
Yangi URL yasab o'ting |
… useSearchParams() … outside of <Suspense> |
Statik sahifada <Suspense> siz |
Komponentni <Suspense> ga o'rang |
| Hook'lar server komponentda xato beradi | "use client" yo'q |
Kichik client komponentga ajrating |
| Menyuda «Bosh sahifa» doim joriy | startsWith("/") |
/ ni aniq tenglik bilan |
| Filtr bosilganda sahifa tepaga sakraydi | scroll sukut true |
scroll={false} |
redirect() ishlamaydi |
try/catch ichida |
try dan tashqarida |
| Prefetch so'rovlari ko'rinmaydi | next dev |
next build + next start |
| Tugma bilan o'tish, yangi tabda ochib bo'lmaydi | router.push havola o'rnida |
Bosiladigan havola — <Link> |
10. Mashqlar
Mashqlar kurs/mashqlar/23/06-navigatsiya/ da. Jonli yechimlarni «Ishga tushir» bilan sinang.
1-mashq (oson): qaysi vosita?
- Menyudagi «Blog» havolasi —
- Forma saqlangandan keyin
/rahmatga o'tish — - Joriy manzil (
/blog/salom) — ?sahifa=2qiymati —- Eski
/portfolioURL'i abadiy/loyihalarga —
Yechim
- Link — foydalanuvchi bosadigan havola; prefetch va
<a>imkoniyatlari bilan. - router.push — kod natijasida o'tish.
- usePathname — client komponentda joriy yo'l.
- useSearchParams — faqat o'qish;
.get("sahifa"). - permanentRedirect — 308: Google kuchni yangi manzilga o'tkazadi.
2-mashq (o'rta): sahifalash
Blog ro'yxati uchun sahifalash yozing. POSTS — 5 ta post nomi, har sahifada 2 tadan. Joriy sahifa — ?sahifa= dan (Number(...), yo'q yoki noto'g'ri bo'lsa — 1). «Oldingi» va «Keyingi» — <Link>, birinchi sahifada «Oldingi», oxirgisida «Keyingi» chizilmasin. Ekranda: Sahifa 2 / 3 va shu sahifadagi postlar. Ishora: Math.ceil va slice.
Yechim
import Link from "next/link";
import { useSearchParams } from "next/navigation";
const POSTS = ["Salom", "Next.js", "Server", "Kesh", "Deploy"];
const PER_PAGE = 2;
export default function App() {
const raw = Number(useSearchParams().get("sahifa"));
const total = Math.ceil(POSTS.length / PER_PAGE);
const valid = Number.isInteger(raw) && raw >= 1 && raw <= total;
const page = valid ? raw : 1;
const items = POSTS.slice((page - 1) * PER_PAGE, page * PER_PAGE);
return (
<>
<p>
Sahifa {page} / {total}
</p>
<ul>
{items.map((p) => (
<li key={p}>{p}</li>
))}
</ul>
<nav>
{page > 1 && (
<Link href={`?sahifa=${page - 1}`}>Oldingi</Link>
)}{" "}
{page < total && (
<Link href={`?sahifa=${page + 1}`}>Keyingi</Link>
)}
</nav>
</>
);
}Ekranda:
Sahifa 3 / 3
Deploy
Oldingi?sahifa=abc yoki ?sahifa=99 kelsa — 1-sahifa: URL'dagi qiymatga ishonmaymiz, tekshiramiz (URL — holat saqlash joyi darsidagi qoida). href="?sahifa=2" — faqat query: yo'l (/blog) o'zgarmaydi.
3-mashq (qiyin): boshqa parametrlarni saqlab qolish
Teg filtrini kengaytiring: URL'da ?teg= dan tashqari ?tartib=yangi ham bo'lishi mumkin. Teg bosilganda tartib saqlansin. Funksiya yozing: withParam(current, key, value) — joriy URLSearchParams nusxasini oladi, key ni value ga o'rnatadi (value === null — o'chiradi) va "?…" satr qaytaradi. Boshlang'ich manzil: ih.boshla("/loyihalar?tartib=yangi") (o'quv modeli sozlamasi). Tugmalar: «React», «HTML», «Hammasi».
Yechim
import Link from "next/link";
import { ih, useSearchParams } from "next/navigation";
ih.boshla("/loyihalar?tartib=yangi");
function withParam(
current: URLSearchParams,
key: string,
value: string | null,
): string {
const next = new URLSearchParams(current.toString());
if (value === null) next.delete(key);
else next.set(key, value);
const query = next.toString();
return query ? `?${query}` : "?";
}
export default function App() {
const params = useSearchParams();
console.log("query:", params.toString());
return (
<nav>
<Link href={withParam(params, "teg", "React")}>React</Link>{" "}
<Link href={withParam(params, "teg", "HTML")}>HTML</Link>{" "}
<Link href={withParam(params, "teg", null)}>Hammasi</Link>
</nav>
);
}Konsolda:
query: tartib=yangi
query: tartib=yangi&teg=React
query: tartib=yangi&teg=HTML
query: tartib=yanginew URLSearchParams(current.toString()) — nusxa: useSearchParams qaytargan obyektni o'zgartirib bo'lmaydi (yuqoridagi xato), nusxani esa bemalol. set mavjud kalitni almashtiradi — shuning uchun «HTML» bosilganda teg=React qolmadi. tartib=yangi har qadamda saqlandi.
4-mashq: Portfolio qadami
O'z portfolio ingizga NavHavola (menyuda), OrqagaTugmasi (404 sahifasida) va TegFiltri (/loyihalar da, <Suspense> ichida, zaxira — to'liq ro'yxat) qo'shing. Tekshiring: npm run build jadvalida /loyihalar ○ bo'lib qolsin; next start da /loyihalar?teg=React ochilsin va to'g'ri son chiqsin; DevTools'da JS o'chirib (Command Menu → "Disable JavaScript") /loyihalar ni oching — kartalar baribir ko'rinishi kerak. Keyin commit.
Yechim
Namuna — «Portfolio qadami» bo'limidagi jadval va kanon komponentlari. JS o'chiq bo'lsa filtr tugmalari ishlamaydi (ular client komponentda), lekin fallback dagi to'liq ro'yxat ko'rinadi — bu kutilgan xulq. Build jadvalida /loyihalar ○ emas, boshqa belgi bo'lsa yoki build to'xtasa — <Suspense> ni tekshiring. Commit xabari — kanondagidek yoki o'zingiznikini yozing: bu holat Maxsus fayllar darsidagi o'zgarishlarni ham o'z ichiga oladi.
11. Real ishda
- Do'kon va katalog filtrlari — deyarli doim URL'da (
?kategoriya=…&narx=…&sahifa=2). Sabab: ulashish, orqaga tugmasi, qidiruv indeksi. Uzum, Olx kabi saytlarda manzil satriga qarang — xuddi shu naqsh. - Prefetch narxi — katta jadval yoki cheksiz ro'yxatda yuzlab
<Link>yuzlab so'rov beradi. Real loyihalardaprefetch={false}yoki hover'da prefetch (onMouseEnter+router.prefetch) ko'p uchraydi. - Yo'naltirishlar — sayt tuzilmasi o'zgarganda eski URL'larni 308 bilan yangisiga bog'lash SEO'ning majburiy qismi. Ko'pincha bu
next.config.tsdagiredirects()da yoziladi (next.config chuqur). - Intervyu savoli: "
<Link>varouter.pushfarqi?", "negauseSearchParamsSuspense talab qiladi?" — ikkalasiga javob endi tayyor.
Xulosa
<Link href>—<a>+ qayta yuklashsiz o'tish + prefetch;hrefsatr yoki{ pathname, query };replace,scroll={false}.- Prefetch faqat prod'da: holat 06 da bosh sahifa 10 ta RSC so'rov yubordi, bosish esa yangi so'rovsiz o'tdi.
usePathname— joriy yo'l (menyudagiaria-current);useRouter—push,replace,back,refresh; importnext/navigationdan.useSearchParams— faqat o'qish; o'zgartirish = yangi URL'ga o'tish; statik sahifada<Suspense>shart, aks holda build to'xtaydi.redirect()— 307,permanentRedirect()— 308; ikkalasi xato tashlaydi,try/catchga o'ramang.
Keyingi dars: Route groups, private papkalar va loyiha tashkili — URL'ni o'zgartirmasdan app/ ni tartibga solamiz.
Manbalar
- Next.js hujjati —
<Link>(prefetch, scroll, replace): https://nextjs.org/docs/app/api-reference/components/link (next 16.4.0 paketi ichidagi hujjat, 2026-10-21 da o'qildi) - Next.js hujjati —
useRouter,usePathname,useSearchParams,redirect,permanentRedirect: https://nextjs.org/docs/app/api-reference/functions/use-router - Next.js xato sahifasi — blocking-prerender-client-hook: https://nextjs.org/docs/messages/blocking-prerender-client-hook
- Kanon o'lchovlari:
portfoliov2, holat 06 (filtr oqimi, JS hajmi), yakuniy prefetch o'lchovi (holat 38). Prefetch sanog'i,<Suspense>siz build va redirect statuslari — biz ishga tushirdik (next 16.4.0,next build+next start, Chrome 155).
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!