18-bo‘lim

Marshrutlash

React Router bilan sahifalar, yo'l parametrlari, ichma-ich marshrutlar va Outlet, dasturiy o'tish hamda so'rov qatori.

🕑 12 daqiqa o‘qish 📄 673 so‘z 👁 0 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. Birinchi marshrutlar
  2. Havolalar
  3. Yo'l parametrlari
  4. Ichma-ich marshrutlar
  5. Dasturiy o'tish
  6. So'rov qatori
  7. Faqat kirganlar uchun
  8. Xulosa

React - kutubxona, unda marshrutlash yo'q. Uni alohida qo'shish kerak.

Eng keng tarqalgan yechim - React Router.

Terminal
npm install react-router

Birinchi marshrutlar #

JSX
import { MemoryRouter, Routes, Route } from "react-router";
import { chiz, matn } from "./dom-muhit.mjs";

function Bosh() {
    return <h1 id="sahifa">Bosh sahifa</h1>;
}

function Haqida() {
    return <h1 id="sahifa">Biz haqimizda</h1>;
}

function Topilmadi() {
    return <h1 id="sahifa">404 - topilmadi</h1>;
}

function Ilova({ boshlangichYol }) {
    return (
        <MemoryRouter initialEntries={[boshlangichYol]}>
            <Routes>
                <Route path="/" element={<Bosh />} />
                <Route path="/haqida" element={<Haqida />} />
                <Route path="*" element={<Topilmadi />} />
            </Routes>
        </MemoryRouter>
    );
}

await chiz(<Ilova boshlangichYol="/" />);
console.log(matn("#sahifa"));

await chiz(<Ilova key="2" boshlangichYol="/haqida" />);
console.log(matn("#sahifa"));

await chiz(<Ilova key="3" boshlangichYol="/yoq-bunday" />);
console.log(matn("#sahifa"));
Natija
Bosh sahifa
Biz haqimizda
404 - topilmadi

Uch qism:

KomponentVazifasi
MemoryRouterMarshrutlash muhitini beradi
RoutesMos keladigan bittasini tanlaydi
RouteYo'l va komponentni bog'laydi

path="*" - hech biri mos kelmaganda ishlaydi.

Nega MemoryRouter

Haqiqiy ilovada BrowserRouter ishlatiladi - u brauzerning manzil qatori bilan ishlaydi:

JSX
import { BrowserRouter } from "react-router";

<BrowserRouter>
    <Routes>...</Routes>
</BrowserRouter>

MemoryRouter esa yo'lni xotirada saqlaydi. U ikki joyda kerak: testlarda va bu darslikdagi kabi misollarda - chunki haqiqiy manzil qatori bo'lmagan muhitda ham ishlaydi.

19-bo'limda testlarda ham aynan shuni ishlatamiz.

Havolalar #

JSX
import { MemoryRouter, Routes, Route, Link } from "react-router";
import { chiz, bos, matn } from "./dom-muhit.mjs";

function Sahifa({ nom }) {
    return (
        <div>
            <h1 id="sahifa">{nom}</h1>
            <Link to="/">Bosh</Link>
            <Link to="/haqida">Haqida</Link>
        </div>
    );
}

await chiz(
    <MemoryRouter initialEntries={["/"]}>
        <Routes>
            <Route path="/" element={<Sahifa nom="Bosh sahifa" />} />
            <Route path="/haqida" element={<Sahifa nom="Haqida" />} />
        </Routes>
    </MemoryRouter>,
);

console.log("boshida:", matn("#sahifa"));

const havolalar = [...document.querySelectorAll("a")];
console.log("havola manzillari:",
    havolalar.map((h) => h.getAttribute("href")));

await bos("a[href='/haqida']");
console.log("bosilgandan keyin:", matn("#sahifa"));
Natija
boshida: Bosh sahifa
havola manzillari: [ '/', '/haqida' ]
bosilgandan keyin: Haqida

<Link to="..."> oddiy <a> ga aylanadi, lekin bosilganda sahifa qayta yuklanmaydi.

Yo'l parametrlari #

JSX
import { MemoryRouter, Routes, Route, useParams } from "react-router";
import { chiz, matn } from "./dom-muhit.mjs";

const kitoblar = {
    1: { sarlavha: "O'tkan kunlar", muallif: "Abdulla Qodiriy" },
    2: { sarlavha: "Sarob", muallif: "Abdulla Qahhor" },
};

function Kitob() {
    const { id } = useParams();
    const kitob = kitoblar[id];

    if (!kitob) return <p id="natija">Kitob topilmadi: {id}</p>;
    return <p id="natija">{kitob.sarlavha} - {kitob.muallif}</p>;
}

function Ilova({ yol }) {
    return (
        <MemoryRouter initialEntries={[yol]}>
            <Routes>
                <Route path="/kitoblar/:id" element={<Kitob />} />
            </Routes>
        </MemoryRouter>
    );
}

await chiz(<Ilova yol="/kitoblar/1" />);
console.log(matn("#natija"));

await chiz(<Ilova key="2" yol="/kitoblar/2" />);
console.log(matn("#natija"));

await chiz(<Ilova key="3" yol="/kitoblar/99" />);
console.log(matn("#natija"));
Natija
O'tkan kunlar - Abdulla Qodiriy
Sarob - Abdulla Qahhor
Kitob topilmadi: 99

useParams yo'ldagi :id qismini beradi.

Diqqat: qiymat satr bo'ladi - Node.js darsligining 12-bo'limidagi bilan bir xil qoida. Sonli taqqoslash kerak bo'lsa, Number() bilan aylantiring va tekshiring.

Ichma-ich marshrutlar #

JSX
import { MemoryRouter, Routes, Route, Outlet, Link } from "react-router";
import { chiz, matn, html } from "./dom-muhit.mjs";

function Qobiq() {
    return (
        <div>
            <nav id="menyu">
                <Link to="/panel">Bosh</Link>
                <Link to="/panel/hisobot">Hisobot</Link>
            </nav>
            <main id="mazmun">
                <Outlet />
            </main>
        </div>
    );
}

function PanelBoshi() {
    return <p>Panel boshi</p>;
}

function Hisobot() {
    return <p>Hisobot sahifasi</p>;
}

function Ilova({ yol }) {
    return (
        <MemoryRouter initialEntries={[yol]}>
            <Routes>
                <Route path="/panel" element={<Qobiq />}>
                    <Route index element={<PanelBoshi />} />
                    <Route path="hisobot" element={<Hisobot />} />
                </Route>
            </Routes>
        </MemoryRouter>
    );
}

await chiz(<Ilova yol="/panel" />);
console.log("menyu bormi:", document.querySelector("#menyu") !== null);
console.log("mazmun:", matn("#mazmun"));

await chiz(<Ilova key="2" yol="/panel/hisobot" />);
console.log("menyu bormi:", document.querySelector("#menyu") !== null);
console.log("mazmun:", matn("#mazmun"));
Natija
menyu bormi: true
mazmun: Panel boshi
menyu bormi: true
mazmun: Hisobot sahifasi

Qobiq ikkala sahifada ham qoladi - faqat <Outlet /> ichidagi qism almashadi.

YozuvMa'nosi
<Outlet />Bola marshrut shu yerga chiziladi
<Route index>Ota yo'lining o'zi ochilganda
path="hisobot"Nisbiy yo'l: /panel/hisobot
Ichma-ich marshrutlar takrorlanishni yo'q qiladi

Ichma-ich marshrutlarsiz har bir sahifada menyuni qayta yozish kerak bo'lardi:

JSX
<Route path="/panel" element={<><Menyu /><PanelBoshi /></>} />
<Route path="/panel/hisobot" element={<><Menyu /><Hisobot /></>} />

Bu nafaqat takrorlanish, balki unumdorlik masalasi ham: sahifa almashganda menyu qayta yaratiladi va uning holati - ochiq bo'lim, aylantirish o'rni - yo'qoladi.

Outlet bilan qobiq joyida qoladi.

Dasturiy o'tish #

JSX
import { MemoryRouter, Routes, Route, useNavigate } from "react-router";
import { chiz, bos, matn } from "./dom-muhit.mjs";

function Kirish() {
    const otish = useNavigate();

    function yubor() {
        otish("/profil");
    }

    return (
        <div>
            <h1 id="sahifa">Kirish</h1>
            <button onClick={yubor}>Kirish</button>
        </div>
    );
}

function Profil() {
    const otish = useNavigate();
    return (
        <div>
            <h1 id="sahifa">Profil</h1>
            <button onClick={() => otish(-1)}>Orqaga</button>
        </div>
    );
}

await chiz(
    <MemoryRouter initialEntries={["/"]}>
        <Routes>
            <Route path="/" element={<Kirish />} />
            <Route path="/profil" element={<Profil />} />
        </Routes>
    </MemoryRouter>,
);

console.log(matn("#sahifa"));

await bos("button");
console.log("kirgandan keyin:", matn("#sahifa"));

await bos("button");
console.log("orqaga bosgandan keyin:", matn("#sahifa"));
Natija
Kirish
kirgandan keyin: Profil
orqaga bosgandan keyin: Kirish

useNavigate hodisa ichidan sahifa almashtirish imkonini beradi - masalan, forma muvaffaqiyatli yuborilgandan keyin.

otish(-1) - brauzerdagi "orqaga" tugmasi bilan bir xil.

Chizish paytida o'tmang

useNavigate faqat hodisa ichida yoki effektda chaqirilishi kerak:

JSX
function Yomon() {
    const otish = useNavigate();
    otish("/boshqa");            // XATO: chizish paytida
    return <p>...</p>;
}

Bu chizish paytidagi yon ta'sir - React bunga qarshi ogohlantiradi va natija kutilmagan bo'lishi mumkin.

Shart bo'yicha yo'naltirish kerak bo'lsa, <Navigate /> komponentini ishlating:

JSX
if (!kirgan) return <Navigate to="/kirish" replace />;

Bu tavsifiy va xavfsiz.

So'rov qatori #

JSX
import { MemoryRouter, Routes, Route, useSearchParams } from "react-router";
import { chiz, bos, matn } from "./dom-muhit.mjs";

function Qidiruv() {
    const [parametrlar, ozgartir] = useSearchParams();

    const so = parametrlar.get("so") ?? "";
    const sahifa = Number(parametrlar.get("sahifa") ?? 1);

    return (
        <div>
            <p id="holat">so: "{so}", sahifa: {sahifa}</p>
            <button onClick={() => ozgartir({ so: "react", sahifa: "2" })}>
                Qidirish
            </button>
        </div>
    );
}

await chiz(
    <MemoryRouter initialEntries={["/?so=node&sahifa=3"]}>
        <Routes>
            <Route path="/" element={<Qidiruv />} />
        </Routes>
    </MemoryRouter>,
);

console.log(matn("#holat"));

await bos("button");
console.log(matn("#holat"));
Natija
so: "node", sahifa: 3
so: "react", sahifa: 2

useSearchParams useState ga o'xshaydi, lekin qiymat manzilda saqlanadi.

Holatni manzilga qo'yish - ko'pincha to'g'ri qaror

Qidiruv so'zi, filtrlar, sahifa raqami, ochiq bo'lim - bularni useState da saqlash mumkin, lekin manzilda saqlash yaxshiroq:

  1. Foydalanuvchi havolani ulashishi mumkin;
  2. Sahifa yangilanganda holat yo'qolmaydi;
  3. Brauzerning "orqaga" tugmasi kutilganday ishlaydi;
  4. Xatcho'p qo'yish ma'noli bo'ladi.

Qoida: holat ko'rinishga tegishli bo'lsa (nima ko'rsatilyapti, qanday filtrlangan) - manzilga qo'ying. Faqat vaqtinchalik bo'lsa (ochiq menyu, yozilayotgan matn) - useState da qoldiring.

Faqat kirganlar uchun #

JSX
import { MemoryRouter, Routes, Route, Navigate } from "react-router";
import { chiz, matn } from "./dom-muhit.mjs";

function Himoya({ kirgan, children }) {
    if (!kirgan) return <Navigate to="/kirish" replace />;
    return children;
}

function Ilova({ kirgan }) {
    return (
        <MemoryRouter initialEntries={["/profil"]}>
            <Routes>
                <Route path="/kirish" element={<h1 id="sahifa">Kirish</h1>} />
                <Route
                    path="/profil"
                    element={
                        <Himoya kirgan={kirgan}>
                            <h1 id="sahifa">Profil</h1>
                        </Himoya>
                    }
                />
            </Routes>
        </MemoryRouter>
    );
}

await chiz(<Ilova kirgan={false} />);
console.log("kirmagan:", matn("#sahifa"));

await chiz(<Ilova key="2" kirgan={true} />);
console.log("kirgan:", matn("#sahifa"));
Natija
kirmagan: Kirish
kirgan: Profil

<Navigate replace /> tarixda yangi yozuv qoldirmaydi - shuning uchun "orqaga" bosganda foydalanuvchi himoyalangan sahifaga qaytib tushmaydi.

Mijozdagi himoya - himoya emas

Yuqoridagi kod interfeys uchun foydali, lekin u xavfsizlik emas.

Hamma React kodi brauzerda ishlaydi - foydalanuvchi uni o'qiy, o'zgartira va chetlab o'ta oladi.

Haqiqiy himoya serverda: har bir so'rovda kim so'rayotgani tekshiriladi va ruxsat bo'lmasa 401 yoki 403 qaytariladi. Node.js darsligining 17-bo'limida buni ko'rgansiz.

Mijozdagi tekshiruv faqat qulaylik uchun: foydalanuvchi ko'ra olmaydigan sahifani ochib, bo'sh ekranga qaramasin.

Amaliy topshiriq
  1. Uchta marshrut va 404 sahifasini yozing.
  2. Link bilan sahifalar orasida yuring.
  3. Oddiy <a> ishlatib, farqni o'ylang.
  4. useParams bilan yo'l parametrini oling.
  5. Mavjud bo'lmagan id uchun xabar chiqaring.
  6. Outlet bilan umumiy qobiq yasang.
  7. index marshrutini qo'shing.
  8. useNavigate bilan tugmadan o'ting.
  9. useSearchParams bilan filtrlarni manzilga qo'ying.
  10. Navigate bilan himoyalangan marshrut yozing.

Xulosa #

  • React da marshrutlash yo'q - React Router qo'shiladi.
  • Routes mos keladigan bitta Route ni tanlaydi; path="*" - 404.
  • <Link> sahifani qayta yuklamaydi; oddiy <a> yuklaydi.
  • useParams yo'l qismini beradi va u satr bo'ladi.
  • <Outlet /> ichma-ich marshrutlar uchun; qobiq joyida qoladi.
  • useNavigate - hodisa ichidan; chizish paytida <Navigate />.
  • Ko'rinishga tegishli holatni manzilda saqlang.
  • Mijozdagi himoya - xavfsizlik emas; haqiqiy tekshiruv serverda.

Keyingi bo'limda testlarni o'rganamiz.

Xatolik topdingizmi?

Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.