IlmHamroh
JavaScript Full-stack/19-qism. React: routing, server state, formalar6/36-dars19 daqiqa
Mundarija (28)

React Router framework mode: routes.ts, route modullari, typegen va SSR/SPA rejimlari

Qisqacha: Framework mode — data mode ustiga qo'yilgan Vite plagini (@react-router/dev). Marshrutlar app/routes.ts da fayllarga bog'lanadi, har fayl — route modul: default komponent, loader/clientLoader, action, ErrorBoundary, meta. react-router typegen har marshrut uchun turlarni o'zi yasaydi (Route.ComponentProps). Rejimlar: SSR (server HTML beradi), SPA (ssr: false) va prerender (HTML build paytida). Bu — Remix'ning davomi: Remix 2 React Router 7 ga birlashgan.

Bu darsda

  • Framework mode data mode'dan nimasi bilan farq qilishini va u Remix bilan qanday bog'liqligini tushuntirasiz.
  • react-router.config.ts, app/root.tsx, app/routes.ts va route modul fayllarining vazifasini bilasiz.
  • loader va clientLoader farqini, typegen va href() nima berishini ko'rasiz.
  • SSR, SPA va prerender rejimlarini taqqoslab, loyihaga mosini tanlay olasiz.
  • vazifalar uchun framework mode o'lchovini o'qiysiz va nega u kanonga kirmaganini tushuntira olasiz.

Oldin bilishingiz kerak: React Router IV: data mode — loader, action va xatolar, Vite II: vite.config, tsconfig frontend loyihada.

1. Nega bu kerak?

Oldingi darsda vazifalar ning vazifa sahifasi loader, action va errorElement oldi. Ishlayapti. Lekin Sardor uchta noqulaylikni sezdi:

  1. Marshrutlar fayli o'syapti. marshrutlar.tsx da har sahifa uchun import, loader, action, errorElement — yigirma sahifada bu fayl ikki yuz qator bo'ladi.
  2. Turlarni qo'lda bog'lash. useLoaderData<typeof vazifaLoader>() — har safar loader nomini eslab yozish kerak. Unutsangiz, vazifa — unknown.
  3. Birinchi kadr bo'sh. Brauzer avval bo'sh HTML oladi, keyin JS yuklanadi, keyin loader ishlaydi. Google va Telegram havola oldindan ko'rinishi uchun ham sahifa bo'sh.

React Router'ning uchinchi rejimi — framework mode — shu uchalasini hal qiladi. U data mode'ning ustiga quriladi: o'sha loader, action, <Form>. Faqat "qayerga nima yoziladi" ni konvensiya (kelishuv) belgilaydi va build vositasi qolgan ishni o'zi qiladi.

Hayotiy o'xshatish: data mode — o'zingiz yig'adigan mebel. Hamma detal bor, lekin har burama qayerga kirishini o'zingiz hal qilasiz. Framework mode — ustaxona: "eshik shu yerga, javon shu yerga" degan chizma tayyor. Erkinlik kamroq, lekin tezroq va xatosiz.

2. Remix'dan React Router 7 gacha

Framework mode'ning tarixi bor. Ko'p maqolada "Remix" nomi uchraydi — chalkashmaslik uchun qisqacha:

Yil Nima bo'ldi
2021 React Router mualliflari Remix frameworkini chiqardi: loader, action, <Form>, SSR
2022 React Router 6.4 — data mode: Remix'ning loader/action g'oyasi oddiy SPA'ga ham keldi
2024, noyabr React Router 7: Remix 2 ning imkoniyatlari framework mode sifatida React Router'ga birlashtirildi
2026, iyun React Router 8 — latest; framework mode — @react-router/dev 8

Demak, "Remix 2 loyiha" ni ko'rsangiz, uning davomi — bugungi framework mode. Importlar: @remix-run/react → react-router, @remix-run/dev → @react-router/dev. Chalkashtirmang: 2025-yil 28-mayda mualliflar "Remix" nomini Remix 3 uchun qayta ishlatishini e'lon qildi ("Wake up, Remix!", remix.run/blog). U React'ga bog'lanmagan, noldan qurilayotgan boshqa framework (Preact fork'idan boshlangan) — bu darsga aloqasiz.

3. Framework mode tuzilmasi

Kanon vazifalar ning o'sha sahifalarini (qobiq, ro'yxat, vazifa va 404) alohida tajriba loyihasida framework mode'da qurdi. Fayllar bo'yicha yuramiz — kodlar o'sha tajribadan aynan.

3.1 Paketlar va buyruqlar

package.json (tajriba, aynan):

json
{
  "scripts": {
    "build": "react-router build",
    "tip": "react-router typegen && tsc"
  },
  "dependencies": {
    "@react-router/dev": "8.4.0",
    "react": "19.3.0",
    "react-dom": "19.3.0",
    "react-router": "8.4.0",
    "isbot": "^5"
  }
}
  • @react-router/dev — Vite plagini va react-router buyrug'i (CLI): react-router dev (dev server), react-router build, react-router typegen.
  • isbot — SSR'da "bu so'rov qidiruv botidanmi?" ni aniqlaydi (botga sahifa to'liq tayyor holda beriladi).

Yangi loyiha noldan: npx create-react-router@latest. Lekin latest — kurs qoidasiga zid; versiyani @react-router/dev bilan bir xil qilib bering (Semver).

3.2 Ikki konfiguratsiya fayli

vite.config.ts — oddiy Vite, faqat plagin boshqa (aynan):

ts
import { reactRouter } from "@react-router/dev/vite";
import { defineConfig } from "vite";

export default defineConfig({
  plugins: [reactRouter()],
});

@vitejs/plugin-react o'rniga reactRouter(). U JSX'ni ham o'giradi, marshrutlarni ham o'qiydi.

react-router.config.ts — framework sozlamalari (aynan):

ts
// 19/#06: framework mode — SPA (ssr: false): GitHub Pages kabi
// statik hosting uchun. Marshrutlar — app/routes.ts (fayl
// konfiguratsiyasi)
import type { Config } from "@react-router/dev/config";

export default {
  ssr: false,
} satisfies Config;

ssr: false — server yo'q, faqat statik fayllar. Sukut bo'yicha ssr: true. Rejimlar — «SSR, SPA va prerender» bo'limida. satisfies Config — satisfies darsidagi operator: obyekt turga mos ekanini tekshiradi, lekin turini kengaytirmaydi.

3.3 app/root.tsx — index.html o'rnida

Framework mode'da index.html yo'q. HTML'ning o'zi ham React komponenti (aynan):

tsx
// root.tsx — HTML qobig'i (framework mode'da index.html o'rniga)
import { Links, Meta, Outlet, Scripts } from "react-router";
import type { ReactNode } from "react";

export function Layout({ children }: { children: ReactNode }) {
  return (
    <html lang="uz">
      <head>
        <meta charSet="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <Meta />
        <Links />
      </head>
      <body>
        {children}
        <Scripts />
      </body>
    </html>
  );
}

export default function Ildiz() {
  return <Outlet />;
}

export function HydrateFallback() {
  return <p>Yuklanmoqda…</p>;
}
  • <Meta /> va <Links /> — har sahifaning meta va links eksportlari shu yerga yig'iladi (sarlavha, tavsif, CSS).
  • <Scripts /> — ilovaning JS fayllari. Unutilsa, sahifa ko'rinadi, lekin hech bir tugma ishlamaydi.
  • HydrateFallback — oldingi darsdagi hydrateFallbackElement ning o'zi, faqat eksport sifatida.

3.4 app/routes.ts — marshrutlar xaritasi

Data mode'dagi obyektlar massivi o'rniga — fayl yo'llari (aynan):

ts
// Marshrutlar xaritasi (framework mode): data mode'dagi obyektlar
// o'rniga — fayllar. Kanondagi (marshrutlar.tsx) uch sahifa
import { index, layout, route } from "@react-router/dev/routes";
import type { RouteConfig } from "@react-router/dev/routes";

export default [
  layout("routes/qobiq.tsx", [
    index("routes/royxat.tsx"),
    route("vazifa/:id", "routes/vazifa.tsx"),
    route("*", "routes/topilmadi.tsx"),
  ]),
] satisfies RouteConfig;

Har funksiya tanish narsaning qisqa yozuvi:

routes.ts Data mode'dagi
route("vazifa/:id", fayl) { path: "vazifa/:id", … }
index(fayl) { index: true, … }
layout(fayl, [...]) yo'lsiz layout: { element, children } (03-dars)
prefix("admin", [...]) bolalar yo'llari oldiga admin/

Faylning o'zi esa element, loader, action va boshqalarni beradi. Fayl nomidan marshrut yasaydigan variant ham bor — @react-router/fs-routes paketi (app/routes/vazifa.$id.tsx → /vazifa/:id). Lekin routes.ts — sukut bo'yicha va aniqroq yo'l.

3.5 Route modul — bitta faylda bitta sahifa

app/routes/vazifa.tsx (aynan):

tsx
// vazifa.tsx — /vazifa/:id: clientLoader (SPA'da loader — brauzerda).
// Route.* turlari — "react-router typegen" yasaydi: params.id —
// string, loaderData — clientLoader qaytargani
// (useLoaderData<typeof ...> shart emas)
import { data } from "react-router";
import type { Route } from "./+types/vazifa";
import { VAZIFALAR } from "./royxat.tsx";

export function clientLoader({ params }: Route.ClientLoaderArgs) {
  const v = VAZIFALAR.find((x) => x.id === Number(params.id));
  if (!v) throw data("yo'q", { status: 404 });
  return v;
}

export default function Vazifa({ loaderData }: Route.ComponentProps) {
  return <h2>{loaderData.matn}</h2>;
}

Fayl nomlangan eksportlar orqali "men kimman" deydi. Router ularni nomidan taniydi:

Eksport Vazifasi
default sahifa komponenti
loader / action serverda ishlaydi (SSR yoki build paytida)
clientLoader / clientAction brauzerda ishlaydi
ErrorBoundary errorElement o'rni
HydrateFallback birinchi ochilishdagi kutish
meta, links <title>, tavsif, CSS havolalari
handle, shouldRevalidate qo'shimcha ma'lumot, revalidatsiyani boshqarish

Ikki yangilik bor. Birinchisi — loaderData endi prop: useLoaderData() hook'i ham ishlaydi, lekin prop qulayroq va turi tayyor. Ikkinchisi — import type { Route } from "./+types/vazifa". Bunday fayl papkada yo'q! Uni typegen yasaydi — «Typegen» bo'limida.

3.6 Route modul — aslida route obyekt

Sehr yo'q. Vite plagini route modulni data mode'dagi route obyektiga aylantiradi. Data mode ham obyektda komponentni element emas, komponent sifatida qabul qiladi: Component, ErrorBoundary, HydrateFallback kalitlari. Natija oynasida framework yo'q, lekin uning ishini qo'lda qilib ko'ramiz:

jsx
import {
  createMemoryRouter,
  data,
  Link,
  Outlet,
  RouterProvider,
  useLoaderData,
  useRouteError,
} from "react-router";

const TASKS = [
  { id: 1, text: "Non olish" },
  { id: 2, text: "Sut olish" },
];

// "app/routes/vazifa.tsx" — route modul (eksportlar obyekti)
const taskModule = {
  clientLoader({ params }) {
    const task = TASKS.find((t) => t.id === Number(params.id));
    if (!task) throw data("yo'q", { status: 404 });
    return task;
  },
  default: function Task({ loaderData }) {
    return <h2>{loaderData.text}</h2>;
  },
  ErrorBoundary() {
    return <p>Vazifa topilmadi ({useRouteError().status})</p>;
  },
};

// Plagin qiladigan ish: modul → route obyekt
function fromModule(path, mod) {
  const Page = mod.default;
  return {
    path,
    loader: mod.clientLoader,
    ErrorBoundary: mod.ErrorBoundary,
    // loaderData — prop sifatida
    Component: () => <Page loaderData={useLoaderData()} />,
  };
}

function Shell() {
  return (
    <div>
      <Link to="/">Ro'yxat</Link>
      <Outlet />
    </div>
  );
}

function List() {
  return (
    <p>
      <Link to="/vazifa/2">Sut olish</Link>{" "}
      <Link to="/vazifa/9">Choy olish</Link>
    </p>
  );
}

const router = createMemoryRouter([
  {
    Component: Shell,
    children: [
      { index: true, Component: List },
      fromModule("vazifa/:id", taskModule),
    ],
  },
]);

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

Ekranda:

text
Ro'yxat
Vazifa topilmadi (404)

Uch o'tish: «Sut olish» — vazifa sahifasi, «Ro'yxat» — orqaga, «Choy olish» — /vazifa/9, bunday vazifa yo'q. Oxirgisida modulning ErrorBoundary si ishladi, Shell (yuqoridagi «Ro'yxat» havolasi) joyida qoldi.

fromModule — framework plaginining juda soddalashtirilgan nusxasi. Haqiqiysi ko'proq ish qiladi: har modulni alohida JS fayliga ajratadi (kod bo'lish), turlarni yasaydi, SSR'da loader ni serverga, clientLoader ni brauzerga yuboradi. Lekin mohiyat shu: framework mode = data mode + konvensiya + build.

Tekshirib ko'ring: Route modulda export function loader va export function clientLoader ikkalasi ham bor. SPA rejimida (ssr: false) qaysi biri foydalanuvchi brauzerida ishlaydi?

Javob

clientLoader. Nomidagi client — "mijoz tomonda, brauzerda". loader esa server uchun: SSR rejimida har so'rovda serverda, SPA rejimida esa faqat prerender qilinadigan sahifalarda — build paytida, bir marta. Server yo'q joyda u ishlay olmaydi.

4. Typegen — turlar o'zi yoziladi

4.1 Route.ComponentProps qayerdan keladi?

react-router typegen buyrug'i routes.ts ni va har route modulni o'qiydi. Keyin .react-router/types/ papkasiga har marshrut uchun tur fayli yozadi. tsconfig.json dagi rootDirs uni ./+types/vazifa deb ko'rsatadi (tajriba, aynan):

json
{
  "include": [
    "app",
    ".react-router/types/**/*",
    "react-router.config.ts",
    "vite.config.ts"
  ],
  "compilerOptions": {
    "rootDirs": [".", "./.react-router/types"]
  }
}

Typegen nimani biladi:

  • vazifa/:id yo'lidan — params.id bor va u string. Data mode'da useParams() hamma kalitni string | undefined deb beradi, imlo xatosini (params.idd) ko'rmaydi.
  • clientLoader qaytarishidan — loaderData turi { id: number; matn: string }.
  • routes.ts dagi hamma yo'llardan — qaysi manzillar mavjud.

dev buyrug'i ishlayotganda typegen fayl o'zgarganda o'zi yangilanadi. CI'da va tekshiruvdan oldin — react-router typegen && tsc (tajribadagi tip skripti). Kanon o'lchovi: tsc exit 0 (natijalar/tajriba/06-framework.txt).

4.2 href() — yo'l tekshiruvchi

Oxirgi bandning foydasi — href(). U yo'l shabloni va parametrlardan manzil yasaydi. app/routes/royxat.tsx da (aynan):

tsx
          {/* href() — typegen'dan keyin yo'l va parametr nomi
              tekshiriladi */}
          <Link to={href("/vazifa/:id", { id: String(v.id) })}>
            {v.matn}
          </Link>

href — oddiy funksiya, react-router dan; data mode'da ham bor. Natija oynasida:

jsx
import { href } from "react-router";

console.log(href("/vazifa/:id", { id: "2" }));
console.log(href("/taom/:slug?", {}));
console.log(href("/vazifa/:id", {}));

export default function App() {
  return <p>Manzillar konsolda</p>;
}

Konsolda:

text
/vazifa/2
/taom
Uncaught Error: Path '/vazifa/:id' requires param 'id' but it was not provided

Birinchi ikki qator — tayyor manzillar. :slug? — ixtiyoriy segment (03-dars): berilmasa, tashlab ketiladi. Uchinchisi — xato: "/vazifa/:id yo'li id parametrini talab qiladi, lekin u berilmagan". Data mode'da bu xato faqat ishlaganda chiqadi — foydalanuvchi bosganda. Framework mode'da typegen href ga ro'yxatdagi yo'llar va ularning parametrlarini o'rgatadi: noto'g'ri yo'l yoki unutilgan parametr tsc da qizil bo'ladi, ilova build ham bo'lmaydi.

Maslahat: Shablon satr bilan yozilgan havola (`/vazifa/${id}`) — har qanday satr. /vazifalar/2 deb xato yozsangiz, TypeScript indamaydi, sahifa 404 bo'ladi. href("/vazifa/:id", …) esa yo'l nomini ro'yxatdan tekshiradi. Bu yondashuvning eng to'liq ko'rinishi — TanStack Router darsida.

5. SSR, SPA va prerender

5.1 Uch yo'l — bitta kod

Framework mode'ning eng katta yangiligi — render qayerda bo'lishini tanlash. Kod bir xil, react-router.config.ts dagi ikki sozlama farq qiladi:

flowchart TB
  B["Brauzer: /taom/osh"]
  B --> SSR["SSR (ssr: true)<br/>server loader ishlaydi,<br/>tayyor HTML"]
  B --> SPA["SPA (ssr: false)<br/>bo'sh index.html,<br/>brauzerda clientLoader"]
  B --> PRE["Prerender<br/>HTML build paytida<br/>yasalgan, faylda"]
  SSR --> H["Brauzer: hydration —<br/>HTML jonlanadi"]
  SPA --> H2["Brauzer: hammasini<br/>JS chizadi"]
  PRE --> H

Har birini ochamiz:

  • SSR (Server-Side Rendering) — har so'rovda server loader ni ishlatadi va React'ni serverda chizib, tayyor HTML yuboradi. Brauzer matnni darhol ko'rsatadi, keyin JS kelib uni jonlantiradi — bu hydration (gidratatsiya) deyiladi: tayyor HTML'ga hodisalar ulanadi. Node server kerak.
  • SPA (ssr: false) — biz qilib kelgan yo'l. Build build/client/index.html yasaydi, hammasini brauzer chizadi. Har qanday statik hosting yetadi (GitHub Pages).
  • Prerender — prerender: ["/", "/menyu"] sozlamasi. Ro'yxatdagi sahifalar build paytida bir marta chiziladi va HTML fayl bo'lib saqlanadi. Server kerak emas, lekin sahifa tayyor HTML bilan ochiladi.
SSR SPA Prerender
Birinchi kadr tayyor matn bo'sh, keyin JS tayyor matn
Server Node kerak kerak emas kerak emas
Ma'lumot yangiligi har so'rovda brauzerda build paytidagi
Qachon do'kon, yangiliklar kabinet, panel blog, hujjat, landing

«Bahor» uchun aralash yechim ham mumkin: ssr: false va prerender: ["/", "/menyu"]. Menyu sahifasi Google uchun tayyor HTML, kabinet — oddiy SPA.

Diqqat: SSR'da komponent serverda ham chiziladi. U yerda window, localStorage, document yo'q. Render paytida ularga murojaat qilsangiz, server ReferenceError bilan yiqiladi. Brauzerga xos kod — useEffect ichida yoki clientLoader da. Shu tuzoqning Zustand'dagi ko'rinishi — Zustand II darsida.

5.2 Nega "bo'sh HTML" muhim?

Telegram'ga havola tashlasangiz, u sahifani yuklab, sarlavha va rasmni (Open Graph) oladi. JS'ni ishlatmaydi. SPA'da u faqat index.html dagi umumiy sarlavhani ko'radi: hamma taom havolasi bir xil ko'rinadi. SSR yoki prerender'da esa har sahifaning o'z meta si HTML'da bor. Google ham JS'ni ishlatadi, lekin kechroq va har doim emas.

vazifalar uchun bu muhim emas: shaxsiy ro'yxat qidiruvda chiqmasligi kerak. «Bahor» menyusi uchun — muhim. Tanlov loyihaning kimga ko'rinishiga bog'liq.

6. Kanon tajribasi: vazifalar framework mode'da

6.1 O'lchov

vazifalar ning o'sha uch sahifasi ikki variantda build qilindi: data mode (tajriba/variantlar/router-rr.tsx) va framework mode SPA (tajriba/framework/). Ikkalasida bir xil React 19.3.0, bir xil Vite 8.3.3:

Uch sahifa: brauzer yuklaydigan JS (gzip)
  • faqat React66,5 KB
  • data mode1 fayl97,3 KB
  • framework mode (SPA)12 fayl111,9 KB

Manba: O'lchandi: tajriba/qur.mjs va react-router build, gzip -9, natijalar/tajriba/hajm.txt va 06-framework.txt, 2026-10-14

Framework mode 14.6 KB katta va 12 faylga bo'lingan. Build chiqishidan (aynan, eng kattalari):

text
assets/entry.client-DUPmwnFo.js     214705   66336   57465
assets/errorBoundaries-D2FSGGX1.js   33515   11727   10610
assets/jsx-runtime-YqIuDQru.js       87379   28668   25419
assets/lib-DRLb-wQy.js                7252    2960    2623
assets/manifest-28b52888.js           2247     503     451
JAMI                                347418  111884   97992 (12 fayl)

Ustunlar: xom bayt, gzip, brotli. Qolgan yetti fayl — har route modul alohida (royxat, vazifa-client-loader, qobiq…), har biri 0.5 KB dan kichik. Plagin har marshrutni o'zi ajratdi — lazy ni qo'lda yozish shart emas. Lekin uch sahifali ilovada bu foyda bermaydi: hamma sahifa baribir kichik, fayllar soni esa ko'paydi. Typegen va tsc — toza: params.id ham, loaderData.matn ham turini o'zi oldi.

6.2 Nega kanonga kirmadi

QADAMLAR qarori (19/#06): framework mode vazifalar ga kirmadi. Sabablar:

  • Hajm — +14.6 KB gzip, foydalanuvchi uchun hech narsa qo'shmasdan (SPA rejimi, SSR yo'q).
  • Butun loyihani qayta tuzish. index.html → root.tsx, main.tsx → plagin, marshrutlar → fayllar, Vitest va 159 + 72 brauzer tekshiruvi — yangi tuzilmaga. Ishlab turgan, tekshirilgan kodni ko'chirish xavfi foydadan katta.
  • SSR'siz asosiy foyda — faqat tiplar. GitHub Pages'da server yo'q, vazifalar qidiruvda ham kerak emas.

Bu framework mode'ni kamsitish emas. Yangi loyiha boshlasangiz va unda ochiq sahifalar (do'kon, menyu, blog) bo'lsa — framework mode yaxshi tanlov: SSR yoki prerender, avtomatik kod bo'lish va turlar bir paketda. Xuddi shu g'oyalarni Next.js qismida ham ko'rasiz: fayl asosidagi marshrutlar, server loader, SSR. Bugungi dars o'sha qismga ko'prik.

7. Ko'p uchraydigan xatolar

Belgi Sabab Davosi
Cannot find module './+types/vazifa' typegen ishlamagan react-router typegen (yoki dev ni yoqing), tsconfig da rootDirs
sahifa ko'rinadi, tugmalar ishlamaydi root.tsx da <Scripts /> yo'q <body> oxiriga <Scripts />
window is not defined (SSR) render paytida brauzer obyekti useEffect yoki clientLoader ga ko'chiring
loader SPA'da ishlamaydi ssr: false, server yo'q clientLoader
@remix-run/react topilmadi eski Remix kodi react-router dan import
server va brauzer HTML'i farq qiladi (hydration xatosi) renderda Date.now(), Math.random() qiymatni effektda yoki loader'da hisoblang

8. Mashqlar

Mashqlar kurs/mashqlar/19/06-framework-mode/ da. Har yechimni «Ishga tushir» bilan sinang.

1-mashq (oson): qaysi fayl?

Bo'sh joylarni to'ldiring (fayl nomi bilan):

  1. ssr: false sozlamasi — faylida.
  2. <html>, <head> va <Scripts /> — faylida.
  3. route("vazifa/:id", "routes/vazifa.tsx") — faylida.
  4. export function clientLoader — faylida.
Yechim

react-router.config.ts — butun framework sozlamasi (rejim, prerender). app/root.tsx — HTML qobig'i, index.html o'rni. app/routes.ts — qaysi yo'lga qaysi fayl. app/routes/vazifa.tsx — route modul: sahifaning komponenti, loader'i va xato chegarasi bir joyda. Bu bo'linish — konvensiya: boshqa framework mode loyihasini ochsangiz, aynan shu fayllarni qidirasiz.

2-mashq (o'rta): modulni ulang

Quyidagi menuModule — route modul. Uni fromModule yordamchisi bilan /menyu marshrutiga ulang. fromModule ga HydrateFallback ni ham qo'shing (modulda bor). Boshlang'ich kod hozir faqat "Bosh sahifa" ni ko'rsatadi; «Menyu» havolasi 404 ga olib boradi.

Ishora: «Route modul — aslida route obyekt» bo'limi.

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

const wait = (ms, value) =>
  new Promise((resolve) => setTimeout(() => resolve(value), ms));

const menuModule = {
  clientLoader: () => wait(200, ["Osh", "Manti", "Lag'mon"]),
  default: function Menu({ loaderData }) {
    return <p>Menyu: {loaderData.join(", ")}</p>;
  },
  HydrateFallback: () => <p>Menyu yuklanmoqda…</p>,
};

function fromModule(path, mod) {
  const Page = mod.default;
  return {
    path,
    loader: mod.clientLoader,
    Component: () => <Page loaderData={useLoaderData()} />,
  };
}

const router = createMemoryRouter([
  { path: "/", element: <Link to="/menyu">Menyu</Link> },
  { path: "*", element: <p>404</p> },
]);

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

Ekranda:

text
404
Yechim
jsx
import {
  createMemoryRouter,
  Link,
  RouterProvider,
  useLoaderData,
} from "react-router";

const wait = (ms, value) =>
  new Promise((resolve) => setTimeout(() => resolve(value), ms));

const menuModule = {
  clientLoader: () => wait(200, ["Osh", "Manti", "Lag'mon"]),
  default: function Menu({ loaderData }) {
    return <p>Menyu: {loaderData.join(", ")}</p>;
  },
  HydrateFallback: () => <p>Menyu yuklanmoqda…</p>,
};

function fromModule(path, mod) {
  const Page = mod.default;
  return {
    path,
    loader: mod.clientLoader,
    HydrateFallback: mod.HydrateFallback,
    Component: () => <Page loaderData={useLoaderData()} />,
  };
}

const router = createMemoryRouter([
  { path: "/", element: <Link to="/menyu">Menyu</Link> },
  fromModule("/menyu", menuModule),
  { path: "*", element: <p>404</p> },
]);

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

Ekranda:

text
Menyu: Osh, Manti, Lag'mon

fromModule("/menyu", menuModule) — massivdagi oddiy route obyekt, * dan oldin ham, keyin ham ishlaydi (router eng aniq mosini tanlaydi — 03-dars). HydrateFallback bu yerda ko'rinmaydi: ilova / dan boshlandi, menyuga o'tishda router eski sahifani ushlab turadi. U /menyu to'g'ridan ochilganda kerak — initialEntries: ["/menyu"] bilan sinab ko'ring.

3-mashq (qiyin): o'z routes.ts ingiz

Framework'dagi route, index va layout funksiyalarini o'zingiz yozing. Ular komponent oladi (fayl o'rniga) va data mode route obyektini qaytaradi:

  • route(path, Component, children?) → { path, Component, children }
  • index(Component) → { index: true, Component }
  • layout(Component, children) → { Component, children }

Boshlang'ich kodda routes massivi tayyor. Hozir u ReferenceError: layout is not defined bilan yiqiladi. Uchala funksiyani yozsangiz, ilova ishlaydi.

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

// route, index, layout — shu yerga

const Shell = () => (
  <div>
    <Link to="/">Bosh</Link> <Link to="/aloqa">Aloqa</Link>
    <Outlet />
  </div>
);
const Home = () => <p>«Bahor» oshxonasi</p>;
const Contact = () => <p>Tel: +998 90 000 00 00</p>;

const routes = [
  layout(Shell, [index(Home), route("aloqa", Contact)]),
];

const router = createMemoryRouter(routes);

export default function App() {
  return <RouterProvider router={router} />;
}
Yechim
jsx
import {
  createMemoryRouter,
  Link,
  Outlet,
  RouterProvider,
} from "react-router";

const route = (path, Component, children) => ({
  path,
  Component,
  children,
});
const index = (Component) => ({ index: true, Component });
const layout = (Component, children) => ({ Component, children });

const Shell = () => (
  <div>
    <Link to="/">Bosh</Link> <Link to="/aloqa">Aloqa</Link>
    <Outlet />
  </div>
);
const Home = () => <p>«Bahor» oshxonasi</p>;
const Contact = () => <p>Tel: +998 90 000 00 00</p>;

const routes = [
  layout(Shell, [index(Home), route("aloqa", Contact)]),
];

const router = createMemoryRouter(routes);

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

Ekranda:

text
Bosh Aloqa
Tel: +998 90 000 00 00

Uchala funksiya — bir qatorli obyekt yasovchilar. routes.ts ning haqiqiy funksiyalari ham deyarli shunday: ular fayl yo'lini saqlaydi, komponentni esa plagin build paytida o'sha fayldan oladi. E'tibor bering: layout da path yo'q — yo'lsiz layout. Oxirgi narsa: route da children ixtiyoriy: berilmasa undefined — router uni "bolasi yo'q" deb tushunadi.

4-mashq: vazifalar ga nazar

Bu darsda vazifalar o'zgarmaydi. Darsdagi tajriba fayllarini («Route modul — bitta faylda bitta sahifa» va «app/routes.ts») data mode darsidagi vazifa-marshruti.ts bilan solishtiring. Ikki savolga javob yozing: (1) data mode'dagi vazifaLoader framework'da qaysi faylda va qaysi nomda? (2) Nega vazifa.tsx da useLoaderData<typeof …> yo'q?

Yechim

(1) app/routes/vazifa.tsx da, clientLoader nomida: SPA rejimida server yo'q, loader brauzerda ishlaydi. Data mode'da u marshrutlar massivida loader: vazifaLoader edi. (2) Komponent loaderData ni prop sifatida oladi va uning turi — Route.ComponentProps dan: typegen uni clientLoader ning qaytish turidan yasagan. Qo'lda typeof yozish shart emas — loader o'zgarsa, tur ham o'zi o'zgaradi.

9. Real ishda

  • Shopify — 2022-yilda Remix jamoasini qo'shib olgan. Uning do'kon frameworki Hydrogen 2025.5.0 (2025-yil may) dan Remix 2 o'rniga React Router 7 framework mode ustida.
  • Yangi React loyiha tanlovi — React hujjati (react.dev) yangi ilova uchun framework tavsiya qiladi: Next.js yoki React Router framework mode. Ikkalasi ham loader/server g'oyasida.
  • Migratsiya — "Remix 2 → React Router 7" eng ko'p uchraydigan ish: importlarni almashtirish, vite.config dagi plagin. Data mode'dan framework'ga ham bosqichma-bosqich o'tish mumkin — har marshrutni alohida.
  • Intervyu savollari: "SSR, SPA va SSG (prerender) farqi?", "hydration nima?", "loader va clientLoader qachon?", "Remix va React Router qanday bog'liq?".

Xulosa

  • Framework mode = data mode + Vite plagini (@react-router/dev) + konvensiya. Remix 2 shu nom bilan React Router 7 ga birlashgan.
  • Fayllar: react-router.config.ts (rejim), app/root.tsx (HTML qobig'i), app/routes.ts (route, index, layout, prefix), route modullar.
  • Route modul eksportlari: default, loader/clientLoader, action, ErrorBoundary, HydrateFallback, meta. loaderData — prop.
  • Typegen Route.* turlarini yasaydi; href() yo'l va parametrni tekshiradi.
  • SSR — har so'rovda serverda, SPA — brauzerda, prerender — build paytida. Tanlov — sahifa kimga ko'rinishiga bog'liq.
  • vazifalar da o'lchandi: 111.9 vs 97.3 KB, 12 fayl; SSR'siz foyda faqat tiplar — kanonga kirmadi.

Keyingi dars: Himoyalangan marshrutlar va rolga asoslangan kirish — kirmagan foydalanuvchini kirish sahifasiga yuborish, ?keyin= bilan qaytarish va "miltillash" siz himoya.

Manbalar

  • React Router: Framework Mode — reactrouter.com/start/framework/installation, routing, route-module, rendering (2026-10 holatiga, v8.4.0)
  • React Router: Type Safety (typegen, href) — reactrouter.com/explanation/type-safety
  • React Router: SPA Mode, Pre-Rendering — reactrouter.com/how-to/spa, /how-to/pre-rendering
  • Remix blog: "Merging Remix and React Router" (2024), "Wake up, Remix!" (2025-05-28) — remix.run/blog
  • Shopify: "Hydrogen May 2025 release" (2025.5.0 — React Router 7) — hydrogen.shopify.dev/update/may-2025
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
React Router framework mode: routes.ts, route modullari, typegen va SSR/SPA rejimlari — IlmHamroh