IlmHamroh
JavaScript Full-stack/19-qism. React: routing, server state, formalar4/36-dars30 daqiqa
Mundarija (35)

React Router navigatsiya: useNavigate, replace, useSearchParams, useLocation va nisbiy yo'llar

Qisqacha: const navigate = useNavigate() — koddan boshqa sahifaga o'tish: navigate("/buyurtma/12"), tarixda orqaga — navigate(-1). { replace: true } tarixga yangi yozuv qo'shmaydi, joriysini almashtiradi — yo'naltirish va kirishdan keyin shunday. Render paytida yo'naltirish — <Navigate to="…" replace />. useLocation() joriy manzilni beradi: pathname, search, state, key. useSearchParams() — ?turi=ichimlik kabi parametrlarni o'qish va yozish: holat URL'da tursa, havola ulashilganda va «Orqaga» bosilganda saqlanadi.

Bu darsda

  • useNavigate bilan amal tugagach o'tasiz, navigate(-1) bilan orqaga qaytasiz.
  • replace va <Navigate> bilan tarixni "ifloslantirmasdan" yo'naltirasiz.
  • useLocation va state, useSearchParams bilan URL holatini o'qib-yozasiz.
  • Nisbiy yo'llarni ("..", relative="path") to'g'ri yozasiz va vazifalar ga aqlli «Orqaga» qo'shasiz.

Oldin bilishingiz kerak: React Router II: dinamik, ichma-ich va layout marshrutlar, location va History API, URL va URLSearchParams.

1. Nega bu kerak?

Oldingi darsda «Bahor» saytidagi hamma o'tishlar havola orqali edi. Endi Jasur aka uch narsa so'radi:

  1. "«Buyurtma berish» bosilsin, buyurtma saqlansin va mehmon o'zi tasdiq sahifasiga o'tsin." Havola yo'q — tugma bor, o'tish saqlashdan keyin.
  2. "Taom sahifasida «Orqaga» bo'lsin." Qayerga? Menyudan kelgan bo'lsa — menyuga. Telegram'dagi havoladan kirgan bo'lsa — orqada saytimiz yo'q.
  3. "«Ichimliklar» ni tanlab havolani do'stimga yuborsam, u ham ichimliklarni ko'rsin." Tanlov useState da bo'lsa, havolada u yo'q.

Uchalasining javobi — router'ning navigatsiya hook'lari. Hayotiy o'xshatish: taksi. <Link> — yo'lovchi joyni xaritadan o'zi ko'rsatadi. useNavigate — haydovchi buyurtma tugagach o'zi keyingi manzilga yuradi. URL parametrlari — yo'l varaqasi: kim olsa, o'sha yo'nalishni ko'radi.

2. useNavigate — koddan o'tish

2.1 Amal tugagach — yangi sahifa

useNavigate() — funksiya qaytaradigan hook. Shu funksiyaga manzil berasiz, router o'sha sahifaga o'tadi. Odatda u onClick yoki onSubmit ichida, amal tugagandan keyin chaqiriladi.

Natija oynasida manzil satri yo'q — oldingi darslardagidek xotiradagi router va ekranga manzilni yozadigan kichik Address komponenti ishlatamiz. Loyihada — createBrowserRouter, hook'lar bir xil.

jsx
import {
  createMemoryRouter,
  Outlet,
  RouterProvider,
  useLocation,
  useNavigate,
  useParams,
} from "react-router";

function Shell() {
  const { pathname, search } = useLocation();
  return (
    <div>
      <p>Manzil: {pathname + search}</p>
      <Outlet />
    </div>
  );
}

let lastId = 11;

function Cart() {
  const navigate = useNavigate();
  function placeOrder() {
    lastId += 1; // serverga saqlash o'rnida
    navigate(`/buyurtma/${lastId}`);
  }
  return (
    <section>
      <p>Savatda: osh, ko'k choy — 40 000 so'm</p>
      <button onClick={placeOrder}>Buyurtma berish</button>
    </section>
  );
}

function Order() {
  const { id } = useParams();
  return <p>{id}-buyurtma qabul qilindi!</p>;
}

const router = createMemoryRouter([
  {
    path: "/",
    element: <Shell />,
    children: [
      { index: true, element: <Cart /> },
      { path: "buyurtma/:id", element: <Order /> },
    ],
  },
]);

export default function App() {
  return <RouterProvider router={router} />;
}

Ekranda:

text
Manzil: /buyurtma/12
12-buyurtma qabul qilindi!

navigate ga <Link to> dagi narsaning o'zi beriladi — satr manzil. Farqi — qachon: havola — foydalanuvchi bosganda, navigate — kodingiz xohlagan paytda (so'rov tugagach, forma tekshiruvdan o'tgach).

Maslahat: Data router'da navigate() o'tish tugaganda bajariladigan Promise qaytaradi. Uni kutish shart emas, lekin vazifalar dagi ESLint qoidasi "e'tiborsiz qolgan Promise" ni ushlaydi. Shuning uchun kanonda void navigate("/"): void — "natijasi kerak emas, ataylab".

Qaysi birini tanlash — foydalanuvchi uchun muhim:

Vaziyat Tanlov Nega
foydalanuvchi bossa — o'tadi <Link> haqiqiy <a href>: yangi tabda ochish, nusxalash, ekran o'quvchi "havola" deydi
amal tugagach o'tish navigate o'tish natijaga bog'liq (saqlandimi?)
render paytida yo'naltirish <Navigate> quyida, «Render paytida yo'naltirish» bo'limida

Ko'p uchraydigan xato — <button onClick={() => navigate("/menyu")}>. Ishlaydi, lekin yangi tabda ochib bo'lmaydi, Malika (ekran o'quvchi) esa "tugma" eshitadi. Shunchaki boshqa sahifaga olib boradigan narsa — doim <Link>.

2.3 Render paytida emas

navigate ni komponent tanasida — chizish paytida — chaqirib bo'lmaydi. Render "ekranda nima bo'lishini hisoblash", yon ta'sir emas (Pure komponentlar). Sardor "kirmagan bo'lsa — kirish sahifasiga" ni shunday yozdi:

jsx
function Cabinet() {
  const navigate = useNavigate();
  if (!loggedIn) navigate("/kirish"); // ❌ render ichida
  return <p>Shaxsiy kabinet</p>;
}

Router o'tishni bajarmaydi — ekranda baribir "Shaxsiy kabinet", konsolda esa ogohlantirish:

text
You should call navigate() in a React.useEffect(), not when your component is first rendered.

Tarjimasi: "navigate() ni komponent birinchi chizilayotganda emas, React.useEffect() ichida chaqiring." Bu holat uchun yanada yaxshi yo'l bor — <Navigate>, «Render paytida yo'naltirish» bo'limida.

2.4 Tarix bo'ylab: navigate(-1)

navigate son ham qabul qiladi: -1 — tarixda bir qadam orqaga (brauzerning «Orqaga» tugmasi), 1 — oldinga. Bu History API dagi history.go(-1) ning router versiyasi:

jsx
<button onClick={() => navigate(-1)}>Orqaga</button>

Jonli ko'rinishi — keyingi bo'limdagi misolda. Nega navigate("/") emas? Menyuda filtr bo'lsa (/?turi=issiq), -1 mehmonni aynan o'sha filtr bilan qaytaradi, brauzer aylantirilgan joyni ham tiklaydi. navigate("/") esa yangi, toza menyuni ochadi — filtr yo'qoladi.

Lekin -1 ning tuzog'i bor. Mehmon /taom/osh ni Telegram'dagi havoladan to'g'ridan ochgan bo'lsa, tarixda saytimizdan oldin — Telegram yoki bo'sh tab. «Orqaga» uni saytdan chiqarib yuboradi. (Natija oynasida buni ko'rmaysiz: xotiradagi tarix birinchi yozuvdan orqaga o'tmaydi.) Davosi — «Vazifalar qadami» da.

Tekshirib ko'ring: Mehmon menyudan «Osh» ga, keyin «Manti» ga havola orqali o'tdi. Manti sahifasida navigate(-1) chaqirilsa, qayerga tushadi? navigate(-2) chi?

Javob

-1 — osh sahifasiga (tarix: menyu → osh → manti), -2 — menyuga. Raqam — "nechta yozuv orqaga", sahifa nomi emas: foydalanuvchi boshqa yo'l bilan kelsa, -2 boshqa joyga tushiradi. Amalda faqat -1 ishlatiladi.

3. replace — tarixga qo'shmasdan almashtirish

3.1 Tarix — sahifalar to'plami

Brauzer tarixi — ustma-ust qo'yilgan likopchalar kabi. Har yangi sahifa ustiga bitta likopcha qo'shadi (push). «Orqaga» — eng ustkisini oladi. Replace esa ustki likopchani boshqasiga almashtiradi: likopchalar soni o'zgarmaydi.

flowchart LR
  subgraph P["navigate('/kabinet')"]
    direction TB
    P3["/kabinet"] --- P2["/kirish"] --- P1["/"]
  end
  subgraph R["navigate('/kabinet', replace)"]
    direction TB
    R2["/kabinet"] --- R1["/"]
  end

Chapda — oddiy o'tish: /kirish tarixda qoldi. O'ngda — replace: /kirish o'rniga /kabinet yozildi. Ikkalasini bitta misolda sinaymiz: kirish sahifasida ikki tugma — «Oddiy kirish» (push) va «replace bilan kirish». Shell har o'tishda manzilni konsolga yozadi:

jsx
import { useEffect } from "react";
import {
  createMemoryRouter,
  Link,
  Outlet,
  RouterProvider,
  useLocation,
  useNavigate,
} from "react-router";

function Shell() {
  const { pathname } = useLocation();
  useEffect(() => console.log("Manzil:", pathname), [pathname]);
  return (
    <div>
      <p>Manzil: {pathname}</p>
      <Outlet />
    </div>
  );
}

function Login() {
  const navigate = useNavigate();
  return (
    <p>
      <button onClick={() => navigate("/kabinet")}>
        Oddiy kirish
      </button>{" "}
      <button onClick={() => navigate("/kabinet", { replace: true })}>
        replace bilan kirish
      </button>
    </p>
  );
}

function Cabinet() {
  const navigate = useNavigate();
  return (
    <p>
      Kabinet <button onClick={() => navigate(-1)}>Orqaga</button>
    </p>
  );
}

const router = createMemoryRouter([
  {
    path: "/",
    element: <Shell />,
    children: [
      { index: true, element: <Link to="/kirish">Kirish</Link> },
      { path: "kirish", element: <Login /> },
      { path: "kabinet", element: <Cabinet /> },
    ],
  },
]);

export default function App() {
  return <RouterProvider router={router} />;
}

Konsolda:

text
Manzil: /
Manzil: /kirish
Manzil: /kabinet
Manzil: /kirish
Manzil: /kabinet
Manzil: /

Ekranda:

text
Manzil: /
Kirish

Konsolni tarix bilan yonma-yon o'qiymiz ([ ] — joriy yozuv):

Qadam Tarix Ekranda
ochilish [/] Kirish havolasi
«Kirish» /, [/kirish] ikki tugma
«Oddiy kirish» (push) /, /kirish, [/kabinet] Kabinet
«Orqaga» /, [/kirish], /kabinet yana kirish sahifasi
«replace bilan kirish» /, [/kabinet], /kabinet Kabinet
«Orqaga» [/], … bosh sahifa

To'rtinchi qator — muammo: foydalanuvchi allaqachon kirgan, «Orqaga» esa uni yana kirish formasiga qaytardi. Haqiqiy ilovada kirish sahifasi "kirgan ekansiz" deb uni yana kabinetga otadi — va «Orqaga» tugmasi "ishlamay" qoladi. replace dan keyin esa /kirish tarixda yo'q: «Orqaga» to'g'ri bosh sahifaga olib keldi. replace qachon kerak? Kirishdan va forma yuborilgandan keyin. Noto'g'ri manzildan to'g'risiga yo'naltirishda. Har harfda URL yangilanadigan qidiruvda.

<Link> da ham shunday: <Link to="/kabinet" replace>.

3.2 Render paytida yo'naltirish: <Navigate>

«Render paytida emas» bo'limidagi muammoga qaytamiz. Chizish paytida "bu sahifa emas, boshqasi" deyish kerak bo'lsa, <Navigate> komponentini qaytaring. U navigate ni o'zi, to'g'ri paytda (chizishdan keyin) chaqiradi:

jsx
import {
  createMemoryRouter,
  Navigate,
  RouterProvider,
  useParams,
} from "react-router";

function Order() {
  const { id } = useParams();
  const number = Number(id);
  // bitta buyurtma — bitta manzil: /buyurtma/012 → /buyurtma/12
  if (Number.isInteger(number) && id !== String(number)) {
    return <Navigate to={`/buyurtma/${number}`} replace />;
  }
  console.log("chizildi:", id);
  return <p>{id}-buyurtma</p>;
}

const router = createMemoryRouter(
  [{ path: "/buyurtma/:id", element: <Order /> }],
  { initialEntries: ["/buyurtma/012"] },
);

export default function App() {
  return <RouterProvider router={router} />;
}

Konsolda:

text
chizildi: 12

Ekranda:

text
12-buyurtma

Oldingi darsdagi savol yechildi: /buyurtma/012 va /buyurtma/12 endi bitta sahifa. "012" raqamga aylanganda 12, String(12) esa "12" — teng emas, demak manzil kanonik emas. <Navigate> darhol to'g'risiga o'tkazdi. "012" bilan sahifa umuman chizilmadi — konsolda faqat bitta qator.

Diqqat: <Navigate> effektda ishlaydi: avval bir kadr chiziladi, keyin o'tish bo'ladi. Ko'pincha bu sezilmaydi. Lekin vazifalar da o'lchandi: kirish sahifasiga <Navigate> bilan yo'naltirilganda birinchi kadr bo'sh edi. Davosi — Himoyalangan marshrutlar darsida.

Tekshirib ko'ring: <Navigate to="/buyurtma/12" /> (replace siz) bilan /buyurtma/012 ochildi. Tarixda nechta yozuv paydo bo'ladi va «Orqaga» nima qiladi?

Javob

Ikkita: /buyurtma/012 va /buyurtma/12. «Orqaga» /buyurtma/012 ga qaytaradi, u yana /buyurtma/12 ga otadi — «Orqaga» go'yo ishlamaydi. Yo'naltirish deyarli doim replace bilan.

4. useLocation — qayerdamiz?

4.1 Manzil obyekti

useLocation() joriy manzilni obyekt qilib qaytaradi. Biz uni darsda Address uchun ishlatdik. To'liq ko'rinishi:

jsx
import {
  createMemoryRouter,
  RouterProvider,
  useLocation,
} from "react-router";

function Where() {
  const location = useLocation();
  console.log(location);
  return <p>{location.pathname}</p>;
}

const router = createMemoryRouter(
  [{ path: "*", element: <Where /> }],
  { initialEntries: ["/menyu?turi=issiq#manti"] },
);

export default function App() {
  return <RouterProvider router={router} />;
}

Konsolda:

text
{pathname: '/menyu', search: '?turi=issiq', hash: '#manti', state: null, key: 'default', mask: undefined}
Maydon Qiymat Nima
pathname "/menyu" yo'l
search "?turi=issiq" so'rov qatori (? bilan)
hash "#manti" sahifa ichidagi joy
state null ko'rinmas ma'lumot (keyingi bo'lim)
key "default" shu tarix yozuvining kaliti

window.location dan farqi — useLocation React'ga ulangan: manzil o'zgarsa, komponent qayta chiziladi (mask bu darsda kerak emas).

key — router har yangi tarix yozuviga beradigan tasodifiy satr ("ht5t5v8l" kabi). Istisno: ilova ochilgan birinchi yozuvning kaliti — "default". Bu «Orqaga» muammosini hal qiladigandek ko'rinadi, lekin replace dan keyin yo'qoladi — «Chekka holat» bo'limida o'lchaymiz.

4.2 location.state — manzilda ko'rinmaydigan ma'lumot

Tasdiq sahifasida buyurtma summasini ko'rsatmoqchimiz. Manzilga yozish (?jami=40000) chiroyli emas, serverdan qayta so'rash — ortiqcha. navigate va <Link> ga state berish mumkin: u tarix yozuvi ichida saqlanadi, manzilda ko'rinmaydi:

jsx
import {
  createMemoryRouter,
  RouterProvider,
  useLocation,
  useNavigate,
} from "react-router";

function Cart() {
  const navigate = useNavigate();
  return (
    <button
      onClick={() =>
        navigate("/tasdiq", {
          replace: true,
          state: { total: "40 000 so'm" },
        })
      }
    >
      Buyurtma berish
    </button>
  );
}

function Thanks() {
  const { state } = useLocation();
  return (
    <p>
      Rahmat! Jami: {state?.total ?? "buyurtmalar tarixida ko'ring"}
    </p>
  );
}

const router = createMemoryRouter([
  { path: "/", element: <Cart /> },
  { path: "/tasdiq", element: <Thanks /> },
]);

export default function App() {
  return <RouterProvider router={router} />;
}

Ekranda:

text
Rahmat! Jami: 40 000 so'm

state?.total ?? … qatoriga e'tibor bering — zaxira matni bor. state mo'rt narsa:

Holat state saqlanadimi
«Orqaga» / «Oldinga» ha (tarix yozuvi ichida)
sahifani yangilash (F5) ha, brauzerda (history.state)
havolani nusxalab yangi tabda ochish yo'q — manzilda yo'q narsa ko'chmaydi
boshqa odamga havola yuborish yo'q

Shuning uchun state — faqat qulaylik uchun; sahifa usiz ham ishlashi shart. Muhim ma'lumot — manzilda (:id, ?parametr) yoki serverda. state — sir ham emas: u brauzer tarixida ochiq turadi, har kim o'zgartira oladi. "Admin ekani" yoki narx kabi qarorlarni unga ishonib qabul qilmang. URL'dagi ?keyin= ni tekshirmasdan navigate ga berish xavfi — Himoyalangan marshrutlar darsida.

Tekshirib ko'ring: Mehmon tasdiq sahifasining havolasini nusxalab, do'stiga yubordi. Do'st nimani ko'radi?

Javob

"Rahmat! Jami: buyurtmalar tarixida ko'ring". Do'stning brauzerida bu tarix yozuvi yo'q — u /tasdiq ni yangi ochdi, state — null. state?.total — undefined, ?? zaxira matnni beradi. Zaxira bo'lmaganda state.total ilovani yiqitardi: Cannot read properties of null (reading 'total').

5. useSearchParams — holat URL'da

5.1 Filtr useState da bo'lsa

Menyuda «Issiq taomlar» va «Ichimliklar» tugmalari bor. Tanlov useState da bo'lsa, u faqat shu tabning xotirasida: sahifa yangilansa yoki havola ulashilsa — yo'qoladi.

URL'ning ? dan keyingi qismi — so'rov parametrlari (search params). Ularni URL va URLSearchParams darsida o'qigansiz: ?turi=ichimlik&q=choy. useSearchParams() ularni React'ga ulaydi. U ikki narsa qaytaradi: o'qish uchun URLSearchParams obyekti va yozish funksiyasi — useState dagi juftlik kabi.

jsx
import {
  createMemoryRouter,
  RouterProvider,
  useLocation,
  useSearchParams,
} from "react-router";

const MENU = [
  { name: "Osh", type: "issiq" },
  { name: "Manti", type: "issiq" },
  { name: "Ko'k choy", type: "ichimlik" },
];

function Menu() {
  const { pathname, search } = useLocation();
  const [searchParams, setSearchParams] = useSearchParams();
  const type = searchParams.get("turi"); // yo'q bo'lsa — null
  const dishes =
    type === null ? MENU : MENU.filter((d) => d.type === type);
  return (
    <section>
      <p>Manzil: {pathname + search}</p>
      <button onClick={() => setSearchParams({})}>
        Hammasi
      </button>{" "}
      <button onClick={() => setSearchParams({ turi: "issiq" })}>
        Issiq taomlar
      </button>{" "}
      <button onClick={() => setSearchParams({ turi: "ichimlik" })}>
        Ichimliklar
      </button>
      <ul>
        {dishes.map((d) => (
          <li key={d.name}>{d.name}</li>
        ))}
      </ul>
    </section>
  );
}

const router = createMemoryRouter(
  [{ path: "/menyu", element: <Menu /> }],
  { initialEntries: ["/menyu"] },
);

export default function App() {
  return <RouterProvider router={router} />;
}

Ekranda:

text
Manzil: /menyu?turi=ichimlik
Hammasi Issiq taomlar Ichimliklar
Ko'k choy

useState yo'q, lekin ro'yxat yangilandi: holatning egasi — URL. setSearchParams manzilni o'zgartiradi, router komponentni qayta chizadi. Endi havolani ulashsa, sahifani yangilasa yoki «Orqaga» bossa — tanlov saqlanadi. setSearchParams ham navigatsiya: tarixga yozuv qo'shadi, ikkinchi argumenti navigate nikiga o'xshaydi ({ replace: true }).

5.2 Boshqa parametrlarni saqlash

setSearchParams({ turi: "issiq" }) butun so'rov qatorini almashtiradi. Manzilda ?q=osh (qidiruv) bor edi deylik — filtr bosilsa, qidiruv yo'qoladi. Birini o'zgartirib, qolganini saqlash uchun funksiya bering. U joriy URLSearchParams ni oladi, siz uni o'zgartirib qaytarasiz:

jsx
import {
  createMemoryRouter,
  RouterProvider,
  useLocation,
  useSearchParams,
} from "react-router";

function Menu() {
  const { search } = useLocation();
  const [searchParams, setSearchParams] = useSearchParams();

  function setParam(name, value) {
    setSearchParams(
      (prev) => {
        if (value === "") {
          prev.delete(name); // bo'sh qiymat URL'da turmasin
        } else {
          prev.set(name, value);
        }
        return prev;
      },
      { replace: name === "q" }, // har harf — tarixga emas
    );
  }

  return (
    <section>
      <p>So'rov: {search}</p>
      <input
        aria-label="Qidiruv"
        value={searchParams.get("q") ?? ""}
        onChange={(e) => setParam("q", e.target.value)}
      />
      <button onClick={() => setParam("turi", "ichimlik")}>
        Ichimliklar
      </button>
    </section>
  );
}

const router = createMemoryRouter(
  [{ path: "/menyu", element: <Menu /> }],
  { initialEntries: ["/menyu"] },
);

export default function App() {
  return <RouterProvider router={router} />;
}

Ekranda:

text
So'rov: ?q=choy&turi=ichimlik
Ichimliklar

Uch odat: funksiya bilan yozish (q saqlandi, turi qo'shildi); bo'sh qiymat — o'chirish (?q= qolmaydi); yozishda replace (aks holda "choy" tarixda to'rt yozuv bo'lardi). Input value si URL'dan o'qiladi — controlled input, faqat state o'rnida URL.

Maslahat: Qachon URL, qachon useState? Savol: "Bu holat havola bilan birga ketishi kerakmi?" Filtr, saralash, qidiruv, sahifa — URL. Forma qoralamasi, ochiq menyu — useState. Chuqurrog'i — URL — holat saqlash joyi darsida.

5.3 Qiymat — satr yoki null

Parametr — satr, yo'q bo'lsa null (useParams da undefined edi). Son kerak bo'lsa — Number(searchParams.get("sahifa") ?? "1") va tekshiruv: URL'ni har kim o'zgartiradi. ?turi=pitsa kelsa, noma'lum qiymatni "hammasi" deb qabul qiling.

6. Nisbiy yo'llar

6.1 / bilan va / siz

Oldingi darsda havolalarni to'liq yozdik: to={`/taom/${slug}/sharhlar`}. Qisqaroq yo'l ham bor. / bilan boshlanmagan to — nisbiy yo'l, u joriy marshrutga nisbatan hal qilinadi:

to /taom/osh marshrutidan Ma'nosi
"/menyu" /menyu mutlaq — ildizdan
"sharhlar" /taom/osh/sharhlar shu marshrutning ichiga
"." /taom/osh shu marshrutning o'zi
".." ota marshrut bir qavat yuqoriga

HTML havolalaridagi nisbiy yo'l kabi (Havola asoslari: ../index.html). Lekin bitta muhim farq bor: .. sukut bo'yicha marshrutlar daraxti bo'yicha yuqoriga chiqadi, URL segmentlari bo'yicha emas.

6.2 .. va relative="path"

Taom tahriri marshruti Shell ning to'g'ridan-to'g'ri bolasi, ichma-ich emas: path: "taom/:slug/tahrir". Tahrir sahifasida ikki «Bekor qilish» havolasi: biri oddiy .., ikkinchisi relative="path" bilan. Qaysi biri qayerga olib borishini havolalarning href i aytadi — Shell ularni har o'tishdan keyin konsolga yozadi:

jsx
import { useEffect } from "react";
import {
  createMemoryRouter,
  Link,
  Outlet,
  RouterProvider,
  useLocation,
} from "react-router";

function Shell() {
  const { pathname } = useLocation();
  useEffect(() => {
    const hrefs = [...document.querySelectorAll("a")].map((a) =>
      a.getAttribute("href"),
    );
    console.log(pathname, "→ havolalar:", hrefs.join(" "));
  }, [pathname]);
  return (
    <div>
      <p>Manzil: {pathname}</p>
      <Outlet />
    </div>
  );
}

function Edit() {
  return (
    <p>
      <Link to="..">Daraxt bo'yicha</Link>{" | "}
      <Link to=".." relative="path">
        URL bo'yicha
      </Link>
    </p>
  );
}

const router = createMemoryRouter(
  [
    {
      path: "/",
      element: <Shell />,
      children: [
        { index: true, element: <p>Menyu</p> },
        {
          path: "taom/:slug",
          element: <Link to="tahrir">Tahrir</Link>,
        },
        { path: "taom/:slug/tahrir", element: <Edit /> },
      ],
    },
  ],
  { initialEntries: ["/taom/osh"] },
);

export default function App() {
  return <RouterProvider router={router} />;
}

Konsolda:

text
/taom/osh → havolalar: /taom/osh/tahrir
/taom/osh/tahrir → havolalar: / /taom/osh
/taom/osh → havolalar: /taom/osh/tahrir

Ekranda:

text
Manzil: /taom/osh
Tahrir

Birinchi qator: <Link to="tahrir"> to'g'ri ishladi — /taom/osh + tahrir. Ikkinchi qator — butun gap. Oddiy .. ning href i — /, ya'ni menyu, taom sahifasi emas. Tahrir marshrutining otasi — Shell (/): daraxtda taom/:slug uning otasi emas. relative="path" bilan .. — URL'dan oxirgi segmentni olib tashlash: /taom/osh/tahrir → /taom/osh. «URL bo'yicha» bosildi va taom sahifasiga qaytdik.

Agar tahrir taom/:slug ning bolasi bo'lganda (oldingi darsdagidek ichma-ich), oddiy .. ham taom sahifasiga olib borardi. Qoida: ichma-ich marshrutda .. tabiiy ishlaydi, yassi marshrutda — relative="path". navigate("..") ham xuddi shunday hal qilinadi, { relative: "path" } bilan.

Nisbiy yo'llarning foydasi: marshrut boshqa joyga ko'chsa (/taom → /menyu/taom), ichidagi havolalar o'zgarmaydi.

Tekshirib ko'ring: /taom/osh/sharhlar marshruti taom/:slug ning bolasi (ichma-ich). Undagi <Link to=".."> qayerga olib boradi? <Link to="../../menyu"> chi?

Javob

.. — ota marshrut taom/:slug, ya'ni /taom/osh. Ichma-ich holatda daraxt va URL mos keladi. ../../menyu — ikki qavat yuqoriga: taom/:slug dan uning otasi / ga, keyin menyu — natija /menyu. Ichma-ich marshrutlarda nisbiy yo'l "papkalar" kabi ishlaydi.

7. Vazifalar qadami: aqlli «Orqaga» va kanonik manzil (holat 04)

7.1 Nima quramiz

Vazifa sahifasidagi <Link to="/">Ro'yxatga qaytish</Link> doim toza ro'yxatga olib boradi: /?filtr=faol&saralash=alifbo dan kelgan foydalanuvchi filtrni ham, saralashni ham yo'qotadi. Uch o'zgarish:

  1. «Orqaga» tugmasi: ilova ichidan kelingan bo'lsa — navigate(-1) (filtr bilan), havola bilan to'g'ridan ochilgan bo'lsa — navigate("/").
  2. /vazifa/02 → /vazifa/2 — <Navigate replace>.
  3. "Ichkaridan kelinganmi?" degan savolga javob — ilova ichidagi tarix chuqurligi: src/tarix-chuqurligi.ts, src/hooklar/useTarixQadami.ts va ildiz layout'da (Qobiq.tsx) bitta chaqiruv. Nega oddiy location.key yetmasligi — «Chekka holat» bo'limida.
bash
git switch -c feat/orqaga

7.2 OrqagaTugmasi

Kanondan aynan (izohlar telefonga sig'ishi uchun qayta bo'lingan):

tsx
// «Orqaga» (19/#04): ilova ichidan kelingan bo'lsa — tarixda bir
// qadam (ro'yxat filtri, saralashi va aylantirilgan joyi bilan),
// havola bilan to'g'ridan ochilgan bo'lsa — ro'yxatga (aks holda
// Orqaga boshqa saytga chiqib ketardi). Qadam — ilova ichidagi
// tarix chuqurligi (tarix-chuqurligi.ts): 0 — orqada ilovaning
// yozuvi yo'q
function OrqagaTugmasi() {
  const navigate = useNavigate();
  const { key } = useLocation();
  return (
    <button
      type="button"
      id="orqaga"
      onClick={() => {
        if (joriyQadam(key) === 0) {
          void navigate("/");
        } else {
          void navigate(-1);
        }
      }}
    >
      Orqaga
    </button>
  );
}
  • joriyQadam(key) — shu yozuvdan orqada ilovaning nechta yozuvi bor. 0 — orqada saytimizdan hech narsa yo'q, demak navigate("/"). Qadamni kim yozishi va nega u bosilgan paytda o'qilishi — «Chekka holat» bo'limida.
  • navigate(-1) — oldingi sahifa o'z filtri va aylantirilgan joyi bilan.
  • Nega <button>, <Link> emas? «Orqaga» ning manzili oldindan noma'lum — href ga yozadigan aniq manzil yo'q.

7.3 Kanonik manzil

VazifaSahifasi ga qo'shimcha (kanon):

tsx
  // Bir vazifa — bitta manzil: /vazifa/02 → /vazifa/2. replace
  // — tarixga yangi yozuv qo'shilmaydi (Orqaga /vazifa/02 ga
  // qaytib, yana shu yerga otib yubormasin)
  if (vazifa !== undefined && id !== String(raqam)) {
    return <Navigate to={`/vazifa/${raqam}`} replace />;
  }

«Render paytida yo'naltirish» bo'limidagi misolning o'zi; /vazifa/99 (yo'q vazifa) yo'naltirilmaydi — "Vazifa topilmadi". Pastdagi havola tugmaga almashadi:

diff
       <Outlet context={kontekst} />
       <p>
-        <Link to="/">Ro'yxatga qaytish</Link>
+        <OrqagaTugmasi />
       </p>

Importlar: Navigate, useLocation, useNavigate va joriyQadam (../tarix-chuqurligi.ts). "Vazifa topilmadi" holatidagi <Link to="/"> o'zgarmaydi.

7.4 Chekka holat: "default" va replace

"Ichkaridan kelinganmi?" degan savolga birinchi xayolga keladigan javob — «Manzil obyekti» bo'limidagi istisno: location.key === "default". Kanonning birinchi varianti shunday edi. Nozik joy: "default" — birinchi yozuvning kaliti, lekin faqat u almashtirilmaguncha. replace yozuvga yangi kalit beradi, tarixda esa o'sha bitta yozuv. Birinchi variantni Chrome 155 da sinadik: yangi tab, manzil, keyin «Orqaga». Nimaga qarash kerak: tugma matnidagi harf (D — kalit "default", B — boshqa) va oxirgi ustun — bosilgandan keyin qayerga tushdik:

text
/vazifa/2 | tugma: Orqaga D idx=0 | bosilgandan keyin: http://127.0.0.1:59975/
/vazifa/02 | tugma: Orqaga B idx=0 | bosilgandan keyin: about:blank

Nima bo'ldi? Ikkinchi qatorda foydalanuvchi saytdan chiqib ketdi — bo'sh tabga. /vazifa/02 → /vazifa/2 almashtirildi, kalit endi "default" emas, tugma navigate(-1) ni tanladi. Ehtimoli kichik (kim 02 yozadi?), lekin data mode da almashtirishni loader'dagi replace() ham qiladi. Belgini almashtiramiz.

Tuzatish: chuqurlikni o'zimiz sanaymiz. Aslida savol "bu birinchi yozuvmi?" emas, "orqada ilovaning yozuvi bormi?". Router har o'tishning turini aytadi — useNavigationType(): "PUSH" (yangi yozuv), "REPLACE" (almashtirish) yoki "POP" (brauzerning Orqaga/Oldinga). Kalit bilan birga shu yetarli: har yozuvga qadam yozamiz — ilova ichida undan orqada nechta yozuv bor.

Tur Qachon Yangi yozuvning qadami
— ilova ochildi 0
"PUSH" havola, navigate("/…") oldingisi + 1
"REPLACE" replace oldingisiniki
"POP" brauzerning Orqaga/Oldinga, sahifa yangilandi yozib qo'yilgani (notanish — 0)

Natija oynasida — o'sha /vazifa/02:

jsx
import { useEffect, useRef } from "react";
import {
  createMemoryRouter,
  Navigate,
  Outlet,
  RouterProvider,
  useLocation,
  useNavigate,
  useNavigationType,
  useParams,
} from "react-router";

// kalit → qadam (kanonda — sessionStorage'da)
const steps = {};
const stepOf = (key) =>
  key !== null && Object.hasOwn(steps, key) ? steps[key] : 0;

// Ildiz layout: har o'tishdan keyin yangi yozuvning qadami
function Root() {
  const type = useNavigationType();
  const { key } = useLocation();
  const prev = useRef(null);
  useEffect(() => {
    if (prev.current === key) return;
    steps[key] =
      type === "PUSH"
        ? stepOf(prev.current) + 1
        : type === "REPLACE"
          ? stepOf(prev.current)
          : stepOf(key);
    const kind = key === "default" ? "default" : "yangi kalit";
    console.log(type, kind, steps[key]);
    prev.current = key;
  }, [type, key]);
  return <Outlet />;
}

function Task() {
  const { id } = useParams();
  const navigate = useNavigate();
  const { key } = useLocation();
  const number = Number(id);
  if (id !== String(number)) {
    return <Navigate to={`/vazifa/${number}`} replace />;
  }
  return (
    <button
      onClick={() =>
        stepOf(key) === 0 ? navigate("/") : navigate(-1)
      }
    >
      Orqaga
    </button>
  );
}

const router = createMemoryRouter(
  [
    {
      element: <Root />,
      children: [
        { path: "/vazifa/:id", element: <Task /> },
        { path: "/", element: <p>Ro'yxat</p> },
      ],
    },
  ],
  { initialEntries: ["/vazifa/02"] },
);

export default function App() {
  return <RouterProvider router={router} />;
}

Konsolda:

text
POP default 0
REPLACE yangi kalit 0
PUSH yangi kalit 1

Ekranda:

text
Ro'yxat

Ikkinchi qator — butun gap: kalit endi "default" emas, qadam esa 0. Tugma ro'yxatga o'tdi (PUSH, 1). if (prev.current === key) return — dev rejimidagi StrictMode effektni ikki marta ishga tushiradi, bitta yozuv ikki marta sanalmasin.

Kanonda xarita src/tarix-chuqurligi.ts da, sessionStorage da turadi. Asosiy qismi (kanondan aynan):

ts
function qadam(xarita: TarixXaritasi, kalit: string | null): number {
  // hasOwn — "constructor" kabi meros kalitlar qadam bo'lib qolmasin
  return kalit !== null && Object.hasOwn(xarita, kalit)
    ? (xarita[kalit] ?? 0)
    : 0;
}

// Yangi yozuvning qadami. PUSH — oldingisidan +1; REPLACE — oldingisi
// (yozuv almashdi, chuqurlik o'sha); POP — Orqaga/Oldinga yoki sahifa
// yangilandi: yozib qo'yilgani. Notanish yozuv, birinchi sahifa — 0
export function qadamHisobi(
  xarita: TarixXaritasi,
  tur: NavigatsiyaTuri,
  oldingi: string | null,
  kalit: string,
): number {
  if (tur === "PUSH") return qadam(xarita, oldingi) + 1;
  if (tur === "REPLACE") return qadam(xarita, oldingi);
  return qadam(xarita, kalit);
}

// … xaritaniOqi, xaritaniOl — sessionStorage va xotiradagi nusxa

// Ildiz layout'dan (useTarixQadami) — har navigatsiyadan keyin
export function qadamniYoz(
  tur: NavigatsiyaTuri,
  oldingi: string | null,
  kalit: string,
): void {
  const joriy = xaritaniOl();
  joriy[kalit] = qadamHisobi(joriy, tur, oldingi, kalit);
  try {
    sessionStorage.setItem(KALIT, JSON.stringify(joriy));
  } catch {
    // Yozilmadi (xotira yopiq/to'la) — xotiradagi nusxa yetadi
  }
}

// 0 — orqada ilovaning yozuvi yo'q: navigate(-1) saytdan chiqarardi
export function joriyQadam(kalit: string): number {
  return qadam(xaritaniOl(), kalit);
}
  • sessionStorage — shu tabniki, sahifa yangilansa ham qoladi: yangilangandan keyin o'tish POP, kalit o'sha — qadam xaritadan o'qiladi.
  • qadamHisobi — sof funksiya, node:test da 8 ta tekshiruv. xaritaniOqi buzuq JSON va son bo'lmagan qiymatni tashlaydi.
  • Xotira yopiq bo'lsa — xotiradagi nusxa ishlaydi; eng yomon holatda qadam 0: tugma ro'yxatga olib boradi, saytdan chiqarmaydi.

Ildiz layout uchun hook (src/hooklar/useTarixQadami.ts, kanondan aynan):

tsx
// useTarixQadami.ts — ildiz layout'da (Qobiq, 19/#04): har
// navigatsiyada yozuvning qadamini yozadi (tarix-chuqurligi.ts).
// Faqat hujjatlangan API: navigatsiya turi (PUSH/REPLACE/POP) va
// location.key. Effekt — chizilgandan keyin; «Orqaga» qadamni
// bosilganda o'qiydi, u paytda yozib bo'lingan
import { useEffect, useRef } from "react";
import { useLocation, useNavigationType } from "react-router";
import { qadamniYoz } from "../tarix-chuqurligi.ts";

export function useTarixQadami(): void {
  const tur = useNavigationType();
  const { key } = useLocation();
  const oldingi = useRef<string | null>(null);
  useEffect(() => {
    // StrictMode effektni ikki marta ishlatadi — bir marta sanalsin
    if (oldingi.current === key) return;
    qadamniYoz(tur, oldingi.current, key);
    oldingi.current = key;
  }, [tur, key]);
}

Qobiq.tsx da — useTarixQadami();. Nega ildiz layout'da? Qobiq hamma sahifada turadi va qayta o'rnatilmaydi — ro'yxatdagi o'tishlar ham sanaladi. VazifaSahifasi.tsx da — bitta shart:

diff
-        if (key === "default") {
+        if (joriyQadam(key) === 0) {

Qadam bosilganda o'qiladi, render paytida emas: effekt chizilgandan keyin ishlaydi, birinchi chizishda qadam hali yozilmagan (render paytida o'qilgani hamma joyda 0 edi). Tuzatilgan mantiq Chrome 155 da — qarang, «bosilganda qadam» va oxirgi ustunga:

text
to'g'ridan /vazifa/02 (→ /vazifa/2) | tugma: Orqaga B, bosilganda qadam=0 → navigate("/", replace) | bosilgandan keyin: /
ro'yxat → havola /vazifa/02 (→ /vazifa/2) | tugma: Orqaga B, bosilganda qadam=1 → navigate(-1) | bosilgandan keyin: /

Birinchi qator — avval bo'sh tabga chiqargan holat, endi ro'yxat. Ikkinchisida replace dan keyin ham qadam 1: ro'yxat o'z filtri bilan qaytadi. (O'lchovda zaxira { replace: true } bilan — kanon keyinroq shunday qiladi.)

Nega history.state.idx emas? Router brauzer tarixiga { usr, key, idx } yozadi, idx — aynan chuqurlik. Lekin u hujjatlarda yo'q — router'ning ichki ishi: keyingi versiyada o'zgarsa, na TypeScript, na testlar ogohlantiradi. history.length ham yaramaydi: boshqa saytlarning yozuvlarini ham sanaydi. Bizning kod faqat hujjatlangan ikki qiymatga (tur va kalit) tayanadi.

7.5 Tekshiruvlar

bash
git add -A
git commit

Commit (kanon): feat: vazifa sahifasida «Orqaga» — tarix yoki ro'yxat; /vazifa/02 → /vazifa/2.

Tekshiruvlar (haqiqiy, holat 04):

  • npm test — node:test 182 (174 + 8 — tekshiruv/tarix-chuqurligi.test.js), Vitest 49.
  • lint, format:check, tip, knip — exit 0. Build: gzip: 114.25 kB (oldin 113.68, +0.6 KB).
  • Brauzer: 159/159, 72/72, 19-qism — 32/32. Yangi oltitasi:
text
✅ Orqaga: ro'yxatdan kelingan — o'sha filtr va saralash bilan: ["/?filtr=faol&saralash=alifbo",["3","1"]]
✅ Orqaga: havola bilan to'g'ridan ochilgan — ro'yxatga: ["/",1]
✅ Navigate replace: /vazifa/02 → /vazifa/2, tarix o'smadi: ["/vazifa/2",0,"Sut olish #bozor"]
✅ navigatsiya: CSP buzilishi yo'q: []
✅ Orqaga: /vazifa/02 yangi tabda (replace) — ilovada, ro'yxatda: ["/",true]
✅ Orqaga: ro'yxatdan kelib, sahifa yangilangach — o'sha filtr bilan: "/?filtr=faol"

Birinchi qator — filtr va saralash bilan qaytish. Uchinchisi — /vazifa/02 → /vazifa/2, tarix o'smadi (0). Beshinchisi — chekka holat: tuzatishdan oldingi kod bilan shu tekshiruv ["about:blank",false] bergan edi. Oltinchisi — sahifa yangilangandan keyin ham qadam sessionStorage da. Diff: 5 fayl, +250 −2 (test bilan).

Maslahat: Ro'yxat filtri hozircha 17-qismdagi o'z pushState kodimiz bilan yoziladi — URL — holat saqlash joyi darsida u router tarixini qanday buzishini o'lchab, useSearchParams ga ko'chiramiz.

8. Ko'p uchraydigan xatolar

Belgi Sabab Davosi
You should call navigate() in a React.useEffect(), not when your component is first rendered. navigate render ichida <Navigate to replace /> qaytaring yoki hodisada chaqiring
«Orqaga» saytdan chiqarib yuboradi sahifa havola bilan ochilgan, navigate(-1) ilova ichidagi qadam 0 — navigate("/")
/vazifa/02 dan «Orqaga» — bo'sh tab replace yangi kalit berdi, "default" yo'qoldi chuqurlikni sanash (useNavigationType + key)
«Orqaga» ishlamaydi, sahifa joyida qoladi yo'naltirish replace siz <Navigate replace> yoki { replace: true }
filtr bosilsa, qidiruv yo'qoladi setSearchParams({ … }) hammasini almashtiradi funksiya bilan: prev.set(…)
qidiruvda har harf — alohida «Orqaga» yozish replace siz setSearchParams(…, { replace: true })
state.total — Cannot read properties of null sahifa yangi tabda ochilgan state?.total ?? zaxira
.. boshqa sahifaga olib boradi marshrut yassi, .. daraxt bo'yicha relative="path" yoki ichma-ich marshrut
searchParams.get("sahifa") + 1 → "21" qiymat — satr Number(…) va tekshiruv

9. Mashqlar

Mashqlar kurs/mashqlar/19/04-navigatsiya/ da. Har yechimni «Ishga tushir» bilan sinang.

1-mashq (oson): tarix qanday bo'ladi?

Mehmon menyu sahifasida (/). Tarixda bitta yozuv: [/]. U ketma-ket quyidagilarni qiladi. Har qadamdan keyin joriy manzilni yozing:

  1. <Link to="/taom/osh"> ni bosdi →
  2. navigate("/savat") →
  3. navigate("/tasdiq", { replace: true }) →
  4. navigate(-1) →
Yechim

Tarix har qadamda:

Qadam Tarix Joriy
1 /, /taom/osh /taom/osh
2 /, /taom/osh, /savat /savat
3 /, /taom/osh, /tasdiq /tasdiq
4 o'sha /taom/osh

Uchinchi qadam /savat ni /tasdiq ga almashtirdi — shuning uchun -1 osh sahifasiga qaytardi. Buyurtma berilgan savatga qaytish ma'nosiz.

2-mashq (o'rta): tanlangan bo'lim URL'da tursin

Quyidagi menyuda bo'lim useState da. Uni useSearchParams ga o'tkazing:

  1. Tanlangan bo'lim — ?bolim=… parametrida (salat yoki shirinlik).
  2. Parametr yo'q yoki noma'lum bo'lsa — salat.
  3. Tugma bosilsa — setSearchParams bilan.

Ishora: «Filtr useState da bo'lsa» bo'limidagi misol. useLocation bilan manzilni ekranga yozing.

jsx
import { useState } from "react";
import { createMemoryRouter, RouterProvider } from "react-router";

const ITEMS = {
  salat: "Achchiq-chuchuk, Vinegret",
  shirinlik: "Chak-chak, Navvot",
};

function Menu() {
  const [section, setSection] = useState("salat");
  return (
    <section>
      <button onClick={() => setSection("salat")}>
        Salatlar
      </button>{" "}
      <button onClick={() => setSection("shirinlik")}>
        Shirinliklar
      </button>
      <p>{ITEMS[section]}</p>
    </section>
  );
}

const router = createMemoryRouter(
  [{ path: "/menyu", element: <Menu /> }],
  { initialEntries: ["/menyu"] },
);

export default function App() {
  return <RouterProvider router={router} />;
}

Ekranda:

text
Salatlar Shirinliklar
Chak-chak, Navvot
Yechim
jsx
import {
  createMemoryRouter,
  RouterProvider,
  useLocation,
  useSearchParams,
} from "react-router";

const ITEMS = {
  salat: "Achchiq-chuchuk, Vinegret",
  shirinlik: "Chak-chak, Navvot",
};

function Menu() {
  const { pathname, search } = useLocation();
  const [searchParams, setSearchParams] = useSearchParams();
  const asked = searchParams.get("bolim");
  const section = asked !== null && asked in ITEMS ? asked : "salat";
  return (
    <section>
      <p>Manzil: {pathname + search}</p>
      <button onClick={() => setSearchParams({ bolim: "salat" })}>
        Salatlar
      </button>{" "}
      <button onClick={() => setSearchParams({ bolim: "shirinlik" })}>
        Shirinliklar
      </button>
      <p>{ITEMS[section]}</p>
    </section>
  );
}

const router = createMemoryRouter(
  [{ path: "/menyu", element: <Menu /> }],
  { initialEntries: ["/menyu?bolim=pitsa"] },
);

export default function App() {
  return <RouterProvider router={router} />;
}

Ekranda:

text
Manzil: /menyu?bolim=shirinlik
Salatlar Shirinliklar
Chak-chak, Navvot

useState ketdi — holatning yagona manbai URL. Ataylab noma'lum ?bolim=pitsa bilan ochildi: ekranda salatlar, ilova yiqilmadi. asked in ITEMS meros kalitlarni ham ko'radi ("toString" in ITEMS — true); qat'iyroq — Object.hasOwn(ITEMS, asked).

3-mashq (qiyin): buyurtma oqimi

Ikki sahifali oqim yasang:

  1. / — Cart: <p>Savat: 2 ta osh</p> va «Tasdiqlash» tugmasi. Bosilsa — /tasdiq ga, replace bilan, state da { count: 2 }.
  2. /tasdiq — Confirm: <p>Qabul qilindi: 2 ta</p> (son state dan) va «Yangi buyurtma» havolasi (/ ga).
  3. state bo'lmasa (tasdiq sahifasi to'g'ridan ochilgan) — <Navigate to="/" replace />.

Ishora: «location.state» va «Render paytida yo'naltirish» bo'limlari. Router initialEntries: ["/tasdiq"] bilan boshlansin — tasdiq sahifasi to'g'ridan ochilgan holat: u savatga yo'naltirilishi kerak.

Yechim
jsx
import {
  createMemoryRouter,
  Link,
  Navigate,
  RouterProvider,
  useLocation,
  useNavigate,
} from "react-router";

function Cart() {
  const navigate = useNavigate();
  return (
    <section>
      <p>Savat: 2 ta osh</p>
      <button
        onClick={() =>
          navigate("/tasdiq", { replace: true, state: { count: 2 } })
        }
      >
        Tasdiqlash
      </button>
    </section>
  );
}

function Confirm() {
  const { state } = useLocation();
  if (state === null) {
    return <Navigate to="/" replace />;
  }
  return (
    <section>
      <p>Qabul qilindi: {state.count} ta</p>
      <Link to="/">Yangi buyurtma</Link>
    </section>
  );
}

const router = createMemoryRouter(
  [
    { path: "/", element: <Cart /> },
    { path: "/tasdiq", element: <Confirm /> },
  ],
  { initialEntries: ["/tasdiq"] },
);

export default function App() {
  return <RouterProvider router={router} />;
}

Ekranda:

text
Qabul qilindi: 2 ta
Yangi buyurtma

Qadamma-qadam:

Qadam Tarix Ekranda
ochilish: /tasdiq, state — null <Navigate replace> → [/] savat
«Tasdiqlash» / almashtirildi → [/tasdiq] Qabul qilindi: 2 ta

Tarixda butun oqim davomida bitta yozuv: ikkala o'tish ham replace. Tasdiq sahifasidan «Orqaga» savatga qaytarmaydi — buyurtmani qayta yuborish xavfi yo'q. state.count ga ?. kerak emas: null holati yuqorida ajratilgan. Bu yerda location.key === "default" ga tayangan «Orqaga» xato qilardi — ikki replace dan keyin kalit "default" emas («Chekka holat»).

4-mashq: Vazifalar qadami

feat/orqaga branch'ida «Vazifalar qadami» bo'limidagi uch o'zgarishni qiling: tarix-chuqurligi.ts va useTarixQadami (testi bilan), Qobiq.tsx da chaqiruv, VazifaSahifasi.tsx da OrqagaTugmasi va kanonik manzil yo'naltirishi. Keyin commit.

Yechim

Tekshiruv ro'yxati (npm run dev):

  • «Faol» + alifbo saralashi → «Batafsil» → «Orqaga»: manzilda ?filtr=faol&saralash=alifbo.
  • Yangi tabda /vazifa/2 (va /vazifa/02) → «Orqaga»: ro'yxat, saytdan chiqmaysiz.
  • Ro'yxatdan vazifaga, F5, «Orqaga» — ro'yxat, filtri bilan.
  • /vazifa/02 → /vazifa/2; /vazifa/99 — "Vazifa topilmadi".
  • npm test — node 182, Vitest 49.

10. Real ishda

  • Kirish oqimlari — navigate(…, { replace: true }) va ?keyin= deyarli har SPA'da bor.
  • Internet-do'kon filtrlari — ?narx=0-100000&rang=qizil&sahifa=2: filtr havolasini ulashsa bo'ladi (URL — holat saqlash joyi).
  • Next.js — useRouter().push/replace/back va useSearchParams (Navigatsiya: Link, useRouter va URL hooklari): g'oyalar o'sha.
  • Analitika — SPA'da sahifa ko'rishi useLocation o'zgarishidan sanaladi.
  • Intervyu savollari: "Link va useNavigate farqi?", "replace qachon kerak?", "location.state nima va qachon yo'qoladi?", "filtrni qayerda saqlaysiz — state yoki URL?".

Xulosa

  • useNavigate() — koddan o'tish, amal tugagach; bosib o'tiladigan joy — <Link>; render ichida — <Navigate>.
  • navigate(-1) — orqaga, filtr bilan. Orqada ilovaning yozuvi yo'q bo'lsa — navigate("/"); buni "default" kalit emas, o'zimiz sanagan chuqurlik aytadi.
  • replace — joriy yozuvni almashtiradi: kirishdan keyin, yo'naltirishda, qidiruvda.
  • useLocation() — pathname, search, hash, state, key; state yangi tabda yo'qoladi va sir emas.
  • useSearchParams() — holat URL'da; qiymat satr yoki null; boshqa parametrlarni saqlash — funksiya bilan.
  • Nisbiy yo'l — marshrut daraxti bo'yicha; URL bo'yicha — relative="path".

Keyingi dars: React Router IV: data mode — loader, action va xatolar — ma'lumotni sahifa chizilishidan oldin loader da yuklash, formani action ga yuborish va xatoni errorElement da ushlash.

Manbalar

  • React Router: Navigating — reactrouter.com/start/data/navigating (2026-10 holatiga, v8.4.0)
  • React Router: useNavigate, Navigate, useLocation, useNavigationType, useSearchParams — reactrouter.com/api/hooks/…, /api/components/Navigate
  • React Router: Link (relative, replace, state) — reactrouter.com/api/components/Link
  • MDN: History.state, History.length, Window.sessionStorage, URLSearchParams — developer.mozilla.org
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
React Router navigatsiya: useNavigate, replace, useSearchParams, useLocation va nisbiy yo'llar — IlmHamroh