IlmHamroh
JavaScript Full-stack/20-qism. React: UI kutubxonalar va animatsiya14/30-dars28 daqiqa
Mundarija (32)

TanStack Table asoslari (Jadvallar I): useTable, ustunlar, saralash, filtr va sahifalash

Qisqacha: TanStack Table — headless jadval. U qatorlarni filtrlaydi, saralaydi va sahifalarga bo'ladi, lekin bitta ham HTML teg chizmaydi: <table>, uslub va a11y sizniki. Versiya 9 da (9.2.6) jadval useTable({ features, columns, data }) bilan yaratiladi: features — tableFeatures({...}) ichida faqat kerakli imkoniyatlar (saralash, filtr, sahifalash) va ularning qator modellari. Ustunlar createColumnHelper bilan yoziladi, ekranga table.getRowModel().rows chiziladi. Eski maqolalar va shadcn DataTable'dagi useReactTable, getCoreRowModel() — v8 API'si.

Bu darsda

  • Headless jadval nima ekanini va TanStack Table aynan nimani o'z zimmasiga olishini tushuntirasiz.
  • tableFeatures, createColumnHelper va useTable bilan birinchi jadvalni chizasiz.
  • Sarlavhani bosib saralashni, aria-sort ni va klaviaturani to'g'ri qilasiz.
  • Qidiruv (global filtr) va o'zbekcha apostroflarni tushunadigan o'z filtr funksiyangizni yozasiz.
  • Sahifalashni qo'shasiz va filtr → saralash → sahifa zanjirini ko'rasiz.
  • v8 kodini (shadcn DataTable) o'qiysiz va v9 ga qanday o'girilishini bilasiz.

Oldin bilishingiz kerak: Headless komponentlar va prop getters, React + TS IV: generic props, shadcn/ui II: real ilovada asosiy komponentlar, Barqaror saralash, TanStack Query I.

1. Nega bu kerak?

«Bahor» admin panelida buyurtmalar ro'yxati jadvalga aylanmoqda. Jasur aka uch narsa so'radi: "narx bo'yicha saralansin", "taom nomi bo'yicha qidirilsin", "bir sahifada 10 tadan bo'lsin".

Sardor buni qo'lda yozdi: uchta useState (saralash, qidiruv, sahifa) va uchta useMemo. Birinchi kun hammasi ishladi. Ikkinchi kun xatolar chiqdi:

  1. 3-sahifada turganda qidiruvga yozdi — natija 1 sahifalik, ekran esa bo'sh: sahifa raqami 3 da qolgan.
  2. Saralash avval sahifani kesib, keyin saraladi — "eng qimmat" faqat birinchi o'ntaning ichida qidirildi.
  3. Har yangi jadvalda (taomlar, mijozlar, kuryerlar) — yana o'sha uchta state va xatolar.

Jadval mantiqi o'zi bir xil: filtrla → sarala → sahifalarga bo'l, tartibi aynan shunday. Faqat ko'rinishi har loyihada boshqacha. Bu — headless kutubxona uchun ideal ish: mantiq kutubxonada, markup sizda.

Hayotiy o'xshatish: kassa apparati ichidagi hisob-kitob. Kassir mahsulot skanerlaydi, chegirmani, soliqni, jamini apparat hisoblaydi. Chekning dizayni (logotip, shrift) esa do'konning o'ziniki. TanStack Table — o'sha hisob-kitob, <table> — chek.

2. Headless jadval qanday ishlaydi

2.1 Qator modellari zanjiri

TanStack Table ma'lumotni bir necha bosqichdan o'tkazadi. Har bosqich — qator modeli (row model): oldingisining natijasini oladi va o'zgartiradi.

flowchart LR
  D["data (massiv)"] --> C["core: hamma qator"]
  C --> F["filtered: qidiruv"]
  F --> S["sorted: saralash"]
  S --> P["paginated: sahifa"]
  P --> R["getRowModel().rows"]
  R --> T["sizning table, tr, td"]

Nimaga qarang: tartib qat'iy — avval filtr, keyin saralash, oxirida sahifa. Sardorning ikkinchi xatosi (avval kesib, keyin saralash) bu zanjirda imkonsiz. Oxirgi o'q — kutubxonadan chiqish: qatorlar tayyor, ularni qanday chizish — sizning ishingiz.

2.2 Versiya: v9 va "eski kodda ko'rsangiz"

Kursda @tanstack/react-table 9.2.6 (2026-yil oktabr). 9-versiya katta o'zgarish bo'ldi va internetdagi maqolalarning ko'pi hali 8-versiyada. Asosiy farq:

v8 (eski kodda ko'rsangiz) v9 (kursda)
Hook useReactTable useTable
Imkoniyatlar hammasi doim ichida tableFeatures({...}) — faqat kerakli
Qator modeli getSortedRowModel: getSortedRowModel() sortedRowModel: createSortedRowModel()
Asosiy model getCoreRowModel() majburiy avtomatik
Holatni o'qish table.getState() table.state
Katakni chizish flexRender(...) <table.FlexRender cell={cell} /> (flexRender ham bor)

Nega v9 imkoniyatlarni alohida ulashni talab qiladi? To'plam hajmi: faqat ro'yxatdagi imkoniyat kodi build'ga kiradi (tree-shaking). v8 da bitta saralash uchun ham guruhlash, pinning, o'lcham kodi kelardi.

3. Birinchi jadval

3.1 Ustunlar va ma'lumot

Jadval uch narsadan yasaladi: features (imkoniyatlar), columns (ustunlar), data (qatorlar). Hali hech qanday imkoniyat yo'q — oddiy jadval:

jsx
import {
  createColumnHelper,
  tableFeatures,
  useTable,
} from "@tanstack/react-table";

// Uchalasi komponentdan TASHQARIDA — har renderda yangi bo'lmasin
const features = tableFeatures({});
const col = createColumnHelper();
const columns = col.columns([
  col.accessor("name", { header: "Taom" }),
  col.accessor("price", {
    header: "Narx",
    cell: (info) => `${info.getValue()} so'm`,
  }),
  col.display({ id: "actions", header: "", cell: () => "⋯" }),
]);
const data = [
  { name: "Osh", price: 35000 },
  { name: "Manti", price: 30000 },
  { name: "Ko'k choy", price: 5000 },
];

export default function App() {
  const table = useTable({ features, columns, data });
  return (
    <table>
      <caption>Menyu</caption>
      <thead>
        {table.getHeaderGroups().map((group) => (
          <tr key={group.id}>
            {group.headers.map((header) => (
              <th key={header.id} scope="col">
                <table.FlexRender header={header} />
              </th>
            ))}
          </tr>
        ))}
      </thead>
      <tbody>
        {table.getRowModel().rows.map((row) => (
          <tr key={row.id}>
            {row.getAllCells().map((cell) => (
              <td key={cell.id}>
                <table.FlexRender cell={cell} />
              </td>
            ))}
          </tr>
        ))}
      </tbody>
    </table>
  );
}

Ekranda:

text
Menyu
Taom Narx
Osh 35000 so'm ⋯
Manti 30000 so'm ⋯
Ko'k choy 5000 so'm ⋯

Har qismni o'qiymiz:

  • col.accessor("name", …) — accessor ustun: qiymati qatordagi name maydonidan olinadi. Saralash va filtr shu qiymat bilan ishlaydi.
  • cell: (info) => … — katak qanday ko'rinishi. info.getValue() — xom qiymat (35000), siz uni formatlaysiz. Saralash esa xom son bilan ishlaydi — "5000 so'm" satri bilan emas.
  • col.display(…) — display ustun: ma'lumoti yo'q, faqat chiziladi (amallar menyusi, belgilash katagi). Unga id majburiy.
  • getHeaderGroups() — sarlavha qatorlari. Odatda bitta qator; ustunlar guruhlansa ("Narx" ostida "Eski" va "Yangi") — bir nechta.
  • table.FlexRender — header yoki cell ning ta'rifini chizadi: matn bo'lsa — matn, funksiya bo'lsa — uni chaqiradi.

Markup to'liq bizniki: caption (jadval nomi — Malika "Menyu, jadval" ni eshitadi), scope="col" (React'da accessibility darsidagi jadval qoidalari). Kutubxona bularni bermaydi va bermasligi kerak — headless.

3.2 Barqaror kirishlar

Kod boshidagi izohga qayting: features, columns va data komponentdan tashqarida. Bu tasodif emas — TanStack hujjatidagi birinchi qoida. Jadval ularni havola bo'yicha solishtiradi (havola tengligi). Har renderda yangi massiv bersangiz, jadval "ma'lumot o'zgardi" deb hamma qator modellarini qaytadan quradi. Eng yomon holatda bu cheksiz qayta render.

Kirish Qayerda Nega
features modul darajasida hech qachon o'zgarmaydi
columns modul darajasida yoki useMemo props'ga bog'liq bo'lsa — useMemo
data state, Query natijasi yoki useMemo o'zgarganda — yangi massiv, aks holda o'sha

Ko'p uchraydigan tuzoq — Query bilan: data: query.data ?? []. Ma'lumot kelguncha har renderda yangi bo'sh massiv yaratiladi. Davosi — modul darajasidagi bitta bo'sh massiv: const EMPTY = []; va data: query.data ?? EMPTY.

4. Saralash

4.1 Imkoniyatni ulash

Saralash uchun uchta narsa tableFeatures ga qo'shiladi: imkoniyat (rowSortingFeature), qator modeli (sortedRowModel) va solishtirish funksiyalari (sortFns). Sarlavha — tugma, bosilganda getToggleSortingHandler():

jsx
import { useEffect } from "react";
import {
  createColumnHelper,
  createSortedRowModel,
  rowSortingFeature,
  sortFn_basic,
  sortFn_text,
  tableFeatures,
  useTable,
} from "@tanstack/react-table";

const features = tableFeatures({
  rowSortingFeature,
  sortedRowModel: createSortedRowModel(),
  sortFns: { basic: sortFn_basic, text: sortFn_text },
});
const col = createColumnHelper();
const columns = col.columns([
  col.accessor("name", { header: "Taom" }),
  col.accessor("price", { header: "Narx" }),
]);
const data = [
  { name: "Osh", price: 35000 },
  { name: "Manti", price: 30000 },
  { name: "Ko'k choy", price: 5000 },
];
const ARIA = { asc: "ascending", desc: "descending" };

export default function App() {
  const table = useTable({ features, columns, data });
  const sorting = table.state.sorting;
  useEffect(() => {
    console.log("sorting:", JSON.stringify(sorting));
  }, [sorting]);
  return (
    <table>
      <thead>
        {table.getHeaderGroups().map((group) => (
          <tr key={group.id}>
            {group.headers.map((header) => {
              const dir = header.column.getIsSorted();
              return (
                <th
                  key={header.id}
                  scope="col"
                  aria-sort={dir ? ARIA[dir] : undefined}
                >
                  <button
                    onClick={header.column.getToggleSortingHandler()}
                  >
                    <table.FlexRender header={header} />
                  </button>
                </th>
              );
            })}
          </tr>
        ))}
      </thead>
      <tbody>
        {table.getRowModel().rows.map((row) => (
          <tr key={row.id}>
            {row.getAllCells().map((cell) => (
              <td key={cell.id}>
                <table.FlexRender cell={cell} />
              </td>
            ))}
          </tr>
        ))}
      </tbody>
    </table>
  );
}

Konsolda:

text
sorting: []
sorting: [{"id":"price","desc":true}]
sorting: [{"id":"price","desc":false}]
sorting: [{"id":"name","desc":false}]

Ekranda:

text
Taom Narx
Ko'k choy 5000
Manti 30000
Osh 35000

Konsol — saralash holati (state): massiv, har elementi { id, desc }. Uni o'qiymiz:

  1. «Narx» birinchi bosish — desc: true, ya'ni kamayish: eng qimmat tepada. Sonli ustun avval kamayish bilan boshlanadi — jadvallarda odatda "eng katta" qidiriladi.
  2. Ikkinchi bosish — o'sish.
  3. «Taom» — matnli ustun avval o'sish bilan (A → Z) boshlanadi. Va «Narx» saralashi almashdi: bitta ustun bilan saralash — standart.

Uchinchi marta «Narx» ni bossangiz, saralash olib tashlanadi ([]) va asl tartib qaytadi. Shift bosib turib boshqa sarlavhani bossangiz — ikki ustun bilan saralash (multi-sort): [{price}, {name}].

Saralash holati table.state.sorting da — v9 da getState() yo'q. Biz uni hech qayerda saqlamadik: jadval o'z ichida ushlab turadi (uncontrolled). Uni tashqariga olish — «Vazifalar qadami» bo'limida.

4.2 aria-sort va klaviatura

Saralash faqat ko'zga emas, quloqqa ham ko'rinishi kerak:

  • Tugma th ichida. th ning o'zi fokus olmaydi. onClick ni th ga qo'ysangiz, Bobur (faqat klaviatura) saralay olmaydi. <button> — Tab bilan keladi, Enter va Space bilan bosiladi.
  • aria-sort — th da, tugmada emas. Qiymati ascending yoki descending. Malika "Narx, ustun sarlavhasi, kamayish tartibida" ni eshitadi.
  • Faqat saralangan ustunda. Saralanmagan ustunga aria-sort="none" yozish shart emas — atributning yo'qligi shu ma'noni beradi. WAI-ARIA tavsiyasi: bir paytda faqat bitta ustunda aria-sort.

Bizning ARIA obyektimiz kutubxona tilini ("asc"/"desc") ARIA tiliga tarjima qiladi. getIsSorted() saralanmaganda false qaytaradi — shuning uchun dir ? ARIA[dir] : undefined.

4.3 Tuzoq: ro'yxatdan o'tmagan sortFn

Oldingi misolda sortFns ga ikkita funksiya yozdik: basic va text. Ustunlarda esa hech biri ko'rsatilmadi. Jadval ustun turiga qarab funksiyani o'zi tanlaydi ("auto"). U birinchi o'nta qatorga qaraydi: satr — text, ichida raqam bor satr — alphanumeric, sana — datetime, son — basic. v9 da tanlangan nom ro'yxatda bo'lishi shart. text ni unutsak — va ma'lumotda bitta taom kichik harf bilan kiritilgan bo'lsa:

jsx
import {
  createColumnHelper,
  createSortedRowModel,
  rowSortingFeature,
  sortFn_basic,
  tableFeatures,
  useTable,
} from "@tanstack/react-table";

const features = tableFeatures({
  rowSortingFeature,
  sortedRowModel: createSortedRowModel(),
  sortFns: { basic: sortFn_basic }, // text yo'q!
});
const col = createColumnHelper();
const columns = col.columns([
  col.accessor("name", { header: "Taom" }),
]);
const data = [{ name: "manti" }, { name: "Osh" }];

export default function App() {
  const table = useTable({ features, columns, data });
  const header = table.getHeaderGroups()[0].headers[0];
  return (
    <div>
      <button onClick={header.column.getToggleSortingHandler()}>
        Taom
      </button>
      <ul>
        {table.getRowModel().rows.map((row) => (
          <li key={row.id}>{row.original.name}</li>
        ))}
      </ul>
    </div>
  );
}

Konsolda:

text
sortFn 'text' (auto) for column 'name' is not registered

Ekranda:

text
Taom
Osh
manti

Tarjimasi: "name ustuni uchun text saralash funksiyasi (avtomatik tanlangan) ro'yxatdan o'tmagan". Xato tashlanmadi, jadval saraladi — lekin noto'g'ri: «Osh» «manti» dan oldin. Nom ro'yxatda yo'q, shuning uchun jadval zaxira funksiyani — basic ni oldi. U esa satrlarni belgi kodlari bilan solishtiradi: barcha katta harflar (O — 79) kichik harflardan (m — 109) oldin keladi. text esa avval ikkalasini kichik harfga o'tkazadi va «manti, Osh» beradi.

Yana bir nozik joy: bu ogohlantirish faqat development rejimida chiqadi. Production build'da konsol jim, tartib esa baribir "katta harflar oldin". Shuning uchun dev'dagi har TanStack ogohlantirishini o'qing.

Davosi ikki xil:

  • sortFns ga text: sortFn_text qo'shish, yoki
  • ustunda aniq aytish: col.accessor("name", { header: "Taom", sortFn: "text" }). Kanonda esa sortFn: "basic" ataylab yozilgan — sabab «Vazifalar qadami» bo'limida.

Nega v9 shunday qattiq? Har built-in funksiya alohida import qilinadi — faqat ishlatilgani to'plamga kiradi. Bepul emas: o'quvchi "auto" nimani tanlashini bilishi kerak.

sortFn_… Nima qiladi
basic oddiy < / > (son, satr)
text satrlarni kichik harfga o'tkazib solishtiradi
alphanumeric "Stol 2" < "Stol 10" (ichidagi sonlarni son deb)
datetime Date qiymatlari

Tekshirib ko'ring: Buyurtma raqamlari "№9", "№10", "№100" satr ko'rinishida. basic bilan o'sish tartibida qanday chiqadi? Qaysi funksiya kerak?

Javob

basic satrlarni belgi-belgi solishtiradi: "№1" bilan boshlangan "№10" va "№100" "№9" dan oldin keladi — №10, №100, №9. Kerakligi — alphanumeric: u satr ichidagi sonlarni son sifatida solishtiradi va №9, №10, №100 beradi. Yaxshisi esa raqamni ma'lumotda son qilib saqlash (9, 10, 100) va "№" ni faqat cell da qo'shish.

5. Filtr va qidiruv

5.1 Global filtr

Global filtr — bitta qidiruv maydoni hamma ustunlar bo'yicha. Unga ikki imkoniyat kerak: columnFilteringFeature (asos) va globalFilteringFeature (u birinchisiga tayanadi — tartib muhim). Qidiruv qiymati setGlobalFilter bilan beriladi:

jsx
import {
  columnFilteringFeature,
  createColumnHelper,
  createFilteredRowModel,
  filterFn_includesString,
  globalFilteringFeature,
  tableFeatures,
  useTable,
} from "@tanstack/react-table";

const features = tableFeatures({
  columnFilteringFeature,
  globalFilteringFeature,
  filteredRowModel: createFilteredRowModel(),
  filterFns: { includesString: filterFn_includesString },
});
const col = createColumnHelper();
const columns = col.columns([
  col.accessor("name", { header: "Taom" }),
  col.accessor("price", { header: "Narx" }),
]);
const data = [
  { name: "Osh", price: 35000 },
  { name: "Manti", price: 30000 },
  { name: "Ko'k choy", price: 5000 },
];

export default function App() {
  const table = useTable({
    features,
    columns,
    data,
    globalFilterFn: "includesString",
  });
  const rows = table.getRowModel().rows;
  return (
    <div>
      <input
        aria-label="Qidiruv"
        value={table.state.globalFilter ?? ""}
        onChange={(e) => table.setGlobalFilter(e.target.value)}
      />
      <p role="status">Topildi: {rows.length}</p>
      <ul>
        {rows.map((row) => (
          <li key={row.id}>{row.original.name}</li>
        ))}
      </ul>
    </div>
  );
}

Ekranda:

text
Topildi: 1
Manti

includesString katta-kichik harfni farqlamaydi: «MAN» — «Manti» ni topdi. role="status" dagi "Topildi: 1" — Malika uchun: u yozayotganda natija soni e'lon qilinadi (Yuklanish, bo'sh va xato holatlari darsidagi qoida).

Global filtr qaysi ustunlarda qidiradi? Standart bo'yicha — qiymati satr yoki son bo'lgan ustunlarda. display ustunlari (amallar) qatnashmaydi.

5.2 O'zbekcha apostrof: o'z filtr funksiyangiz

«Ko'k choy» ni qidirib ko'raylik. Klaviaturada ' yozilsa — topiladi. Lekin telefon klaviaturasi ko'pincha ‘ (U+2018) yoki ʻ (U+02BB, rasmiy o'zbek belgisi) qo'yadi. includesString uchun ular har xil belgi:

jsx
import { useState } from "react";
import {
  columnFilteringFeature,
  createColumnHelper,
  createFilteredRowModel,
  filterFn_includesString,
  globalFilteringFeature,
  tableFeatures,
  useTable,
} from "@tanstack/react-table";

// U+2018, U+2019, U+02BB, U+02BC va ` — hammasi oddiy '
const APOSTROPHES = /[‘’ʻʼ`]/g;
function normalize(text) {
  return String(text).toLowerCase().replace(APOSTROPHES, "'");
}
function uzIncludes(row, columnId, query) {
  return normalize(row.getValue(columnId)).includes(normalize(query));
}

const features = tableFeatures({
  columnFilteringFeature,
  globalFilteringFeature,
  filteredRowModel: createFilteredRowModel(),
  filterFns: {
    includesString: filterFn_includesString,
    uz: uzIncludes,
  },
});
const col = createColumnHelper();
const columns = col.columns([
  col.accessor("name", { header: "Taom" }),
]);
const data = [{ name: "Ko'k choy" }, { name: "Osh" }];

function Search({ fn, query }) {
  const table = useTable({
    features,
    columns,
    data,
    globalFilterFn: fn,
    state: { globalFilter: query },
  });
  const names = table.getRowModel().rows.map((r) => r.original.name);
  return <p>{fn}: {names.join(", ") || "topilmadi"}</p>;
}

export default function App() {
  const [query, setQuery] = useState("");
  return (
    <div>
      <button onClick={() => setQuery("ko‘k")}>
        Telefondan yozish
      </button>
      <Search fn="includesString" query={query} />
      <Search fn="uz" query={query} />
    </div>
  );
}

Ekranda:

text
Telefondan yozish
includesString: topilmadi
uz: Ko'k choy

Bir xil so'rov, ikki natija. O'z filtr funksiyamiz (row, columnId, query) => boolean — qator kerakmi yoki yo'q. Uni filterFns ga uz nomi bilan qo'shdik va globalFilterFn: "uz" bilan tanladik. vazifalar da shu g'oya 18-qismdan beri bor — qidiruv.ts dagi normalla(). Buyruqlar oynasi ham aynan shuni ishlatadi.

Bu misolda state: { globalFilter: query } ga e'tibor bering: qidiruv qiymati jadval ichida emas, ota komponentda. Bu controlled holat (Controlled va uncontrolled) — bitta qiymat ikki jadvalni boshqardi.

5.3 Ustun filtri

Global filtrdan tashqari har ustunning o'z filtri bo'lishi mumkin: "faqat 30 000 dan arzon" yoki "faqat ichimliklar". Ustun obyektida: column.setFilterValue(qiymat) va column.getFilterValue(); ustun ta'rifida — filterFn: "includesString" yoki o'z funksiyangiz. Holat — table.state.columnFilters: [{ id: "category", value: "ichimlik" }] ko'rinishidagi massiv. Bu filtrni mashqda yozasiz.

6. Sahifalash

rowPaginationFeature va paginatedRowModel — qatorlarni sahifalarga bo'ladi. Endi uchala bosqichni birga ko'ramiz: qidiruv, saralash va sahifa. Boshlang'ich holat — initialState (uncontrolled — keyin jadval o'zi boshqaradi):

jsx
import {
  columnFilteringFeature,
  createColumnHelper,
  createFilteredRowModel,
  createPaginatedRowModel,
  createSortedRowModel,
  filterFn_includesString,
  globalFilteringFeature,
  rowPaginationFeature,
  rowSortingFeature,
  sortFn_basic,
  tableFeatures,
  useTable,
} from "@tanstack/react-table";

const features = tableFeatures({
  columnFilteringFeature,
  globalFilteringFeature,
  rowSortingFeature,
  rowPaginationFeature,
  filteredRowModel: createFilteredRowModel(),
  sortedRowModel: createSortedRowModel(),
  paginatedRowModel: createPaginatedRowModel(),
  filterFns: { includesString: filterFn_includesString },
  sortFns: { basic: sortFn_basic },
});
const col = createColumnHelper();
const columns = col.columns([
  col.accessor("name", { header: "Taom", sortFn: "basic" }),
  col.accessor("price", { header: "Narx", sortFn: "basic" }),
]);
const data = [
  { name: "Osh", price: 35000 },
  { name: "Manti", price: 30000 },
  { name: "Lag'mon", price: 28000 },
  { name: "Somsa", price: 8000 },
  { name: "Ko'k choy", price: 5000 },
];

export default function App() {
  const table = useTable({
    features,
    columns,
    data,
    globalFilterFn: "includesString",
    initialState: { pagination: { pageIndex: 0, pageSize: 2 } },
  });
  const { pageIndex } = table.state.pagination;
  const header = (id) =>
    table.getHeaderGroups()[0].headers.find((h) => h.id === id);
  return (
    <div>
      <input
        aria-label="Qidiruv"
        value={table.state.globalFilter ?? ""}
        onChange={(e) => table.setGlobalFilter(e.target.value)}
      />
      <button
        onClick={header("price").column.getToggleSortingHandler()}
      >
        Narx
      </button>
      <ul>
        {table.getRowModel().rows.map((row) => (
          <li key={row.id}>
            {row.original.name} — {row.original.price}
          </li>
        ))}
      </ul>
      <p>
        <button
          onClick={() => table.previousPage()}
          disabled={!table.getCanPreviousPage()}
        >
          Oldingi
        </button>{" "}
        {pageIndex + 1} / {table.getPageCount()} (jami{" "}
        {table.getFilteredRowModel().rows.length}){" "}
        <button
          onClick={() => table.nextPage()}
          disabled={!table.getCanNextPage()}
        >
          Keyingi
        </button>
      </p>
    </div>
  );
}

Ekranda:

text
Narx
Osh — 35000
Lag'mon — 28000
Oldingi 1 / 2 (jami 4) Keyingi

Qadamma-qadam:

Qadam Holat Ekranda
boshida 5 qator, sahifa 1/3 Osh, Manti
«Narx» kamayish bo'yicha Osh, Manti (eng qimmatlar)
«Keyingi» sahifa 2/3 Lag'mon, Somsa
«o» yozildi 4 ta mos (Manti — yo'q), sahifa 1/2 Osh, Lag'mon

Oxirgi qatorga qarang: qidiruvga yozganda sahifa o'zi birinchiga qaytdi. Sardorning birinchi xatosi ("3-sahifada qolib, bo'sh ekran") shu yerda yo'q. Bu avtomatik sahifa tiklash (autoResetPageIndex): ma'lumot yoki filtr o'zgarsa, jadval pageIndex ni 0 qiladi. Ba'zan bu kerak emas (masalan, qatorni tahrirlaganda sahifa qochmasin) — unda autoResetPageIndex: false.

Tekshirib ko'ring: Qidiruvdagi «o» ni o'chirsangiz, qaysi sahifa ochiladi — 2-mi yoki 1-mi? Nega?

Javob

1-sahifa. Filtr qiymati o'zgardi (bo'sh satr ham — o'zgarish), filtrlangan qatorlar o'zgardi va autoResetPageIndex yana pageIndex ni 0 qildi. Avvalgi "2-sahifa" ni jadval eslab qolmaydi — u faqat hozirgi holatni biladi. Foydalanuvchi qaytib kelganda sahifa saqlansin desangiz, sahifani URL'da saqlang va avtomatik tiklashni o'chiring.

7. Holat kimda: jadvalda yoki sizda

Jadvalning har imkoniyati o'z holat bo'lagiga ega: sorting, globalFilter, columnFilters, pagination. Uch yo'l bor:

Yo'l Yozilishi Qachon
Jadval o'zi hech narsa yoki initialState: { … } holat faqat jadval ichida kerak
Controlled state: { sorting } + onSortingChange: setSorting holat tashqarida ham kerak: URL, server so'rovi, boshqa komponent
Faqat o'qish table.state.sorting chizishda ko'rsatish uchun

Controlled rejimda ikkalasi juft bo'ladi: state.sorting va onSortingChange. Faqat onSortingChange bersangiz, jadval holatni yangilaydi. Lekin keyingi renderda u eski qiymatga qaytadi — tugma "ishlamaydi". onSortingChange ga React'ning setSorting ini to'g'ridan-to'g'ri berish mumkin: jadval unga ba'zan qiymat, ba'zan yangilovchi funksiya beradi, useState setter esa ikkalasini ham tushunadi.

useTable ning ikkinchi argumenti — selektor: useTable(options, (state) => ({ pagination: state.pagination })). Bermasangiz, komponent jadval holatining har o'zgarishida qayta chiziladi. Kichik jadvalda bu muammo emas. Minglab qatorli jadvalda esa selektor va table.Subscribe bilan qayta renderlarni toraytirasiz — bu keyingi darsning mavzusi.

8. Eski kodda ko'rsangiz: v8 va shadcn DataTable

shadcn/ui'ning DataTable namunasi va internetdagi ko'p maqolalar v8 bilan yozilgan. Ularni o'qiy olishingiz kerak:

tsx
// v8 — eski kodda ko'rsangiz (shadcn DataTable shunday)
import {
  flexRender,
  getCoreRowModel,
  getPaginationRowModel,
  getSortedRowModel,
  useReactTable,
} from "@tanstack/react-table";

const table = useReactTable({
  data,
  columns,
  getCoreRowModel: getCoreRowModel(),
  getSortedRowModel: getSortedRowModel(),
  getPaginationRowModel: getPaginationRowModel(),
  onSortingChange: setSorting,
  state: { sorting },
});
// katak: flexRender(cell.column.columnDef.cell, cell.getContext())

Xuddi shu jadval v9 da:

tsx
import {
  createPaginatedRowModel,
  createSortedRowModel,
  rowPaginationFeature,
  rowSortingFeature,
  sortFn_basic,
  tableFeatures,
  useTable,
} from "@tanstack/react-table";

const features = tableFeatures({
  rowSortingFeature,
  rowPaginationFeature,
  sortedRowModel: createSortedRowModel(),
  paginatedRowModel: createPaginatedRowModel(),
  sortFns: { basic: sortFn_basic },
});

const table = useTable({
  features,
  columns,
  data,
  onSortingChange: setSorting,
  state: { sorting },
});
// katak: <table.FlexRender cell={cell} />

O'girish tartibi:

  1. useReactTable → useTable; komponentdan tashqarida features.
  2. getCoreRowModel — o'chiriladi (avtomatik).
  3. Har get…RowModel: get…RowModel() → tableFeatures ichida imkoniyat + …RowModel: create…RowModel(). Imkoniyat oldin yoziladi.
  4. sortingFns → sortFns, sortingFn (ustunda) → sortFn; built-in funksiyalarni alohida import qilib ro'yxatga qo'shing.
  5. table.getState() → table.state.
  6. Ustun yordamchisi: createColumnHelper<Order>() → createColumnHelper<typeof features, Order>() (TypeScript'da imkoniyatlar ham turga kiradi).

Migratsiyani bosqichma-bosqich qilish uchun @tanstack/react-table/legacy ichida useLegacyTable bor — v8 ga o'xshash API. Lekin u eskirgan (deprecated) ko'prik: hamma imkoniyatlarni to'plamga qo'shadi. Uni faqat vaqtincha ishlating.

shadcn DataTable'ni v9 bilan ishlatish: npx shadcn add table faqat <Table>, <TableRow>, <TableCell> kabi ko'rinish komponentlarini beradi — ular kutubxonaga bog'liq emas. Jadval mantiqini (yuqoridagi v9 kod) o'zingiz yozasiz. Hujjatdagi v8 misolini yuqoridagi olti qadam bilan o'girasiz.

9. Vazifalar qadami: teglar jadvali (holat 14)

9.1 Nima o'zgaradi

vazifalar ning batafsil statistikasida 18-qismdan beri teglar jadvali bor: teg, jami, bajarilgan. Saralash «Teglar tartibi» tanlovida edi: "Ko'pi oldin" yoki "Alifbo bo'yicha". Endi ustun sarlavhasini bosib ham saralash mumkin. Ikkalasi bitta holatni boshqaradi.

bash
git switch -c feat/teglar-jadvali
npm i -E @tanstack/react-table@9.2.6

Jadval alohida faylga ko'chdi — xususiyatlar/statistika/TeglarJadvali.tsx (162 qator). Asosiy qismlari (kanondan):

tsx
// Imkoniyatlar (features) — modul darajasida, bir marta (v9
// qoidasi: har renderda yangi obyekt bo'lsa, qatorlar modeli
// qayta quriladi). Faqat saralash: filtr, sahifalash kerak emas
// (o'nlab teg)
const imkoniyatlar = tableFeatures({
  rowSortingFeature,
  sortedRowModel: createSortedRowModel(),
  sortFns: { basic: sortFn_basic },
});

const ustun = createColumnHelper<typeof imkoniyatlar, TegHisobi>();
// sortFn "basic" — oddiy < / > (18-qismdagi alifbo tartibi
// bilan bir xil: "#aka" < "#bozor"); sortDescFirst — sonlar
// avval ko'pidan
const ustunlar = ustun.columns([
  ustun.accessor("teg", { header: "Teg", sortFn: "basic" }),
  ustun.accessor("jami", {
    header: "Jami",
    sortFn: "basic",
    sortDescFirst: true,
  }),
  ustun.accessor("bajarildi", {
    header: "Bajarilgan",
    sortFn: "basic",
    sortDescFirst: true,
  }),
]);

// Tanlov ↔ saralash holati. "soni" — saralashsiz: statistika o'zi
// ko'pi oldin beradi (teng bo'lsa — ro'yxatdagi tartib)
const TANLOVLAR = {
  soni: [] as SortingState,
  alifbo: [{ id: "teg", desc: false }] as SortingState,
};

(Izoh qatorlari telefon uchun bo'lingan, kod — kanondan aynan.) Uch qaror:

  • sortFn: "basic", text emas. Teglar normalla() dan o'tgan — hammasi kichik harf. 18-qismdagi "Alifbo bo'yicha" ham < bilan solishtirardi (Intl.Collator emas: Node va Chrome'da o'zbekcha qoidalar har xil). basic aynan shu tartibni saqlaydi — e2e'dagi kutilgan natija o'zgarmaydi.
  • sortDescFirst: true — sonli ustunlar avtomatik ham kamayishdan boshlanadi. Kanon buni aniq yozgan: "auto" birinchi o'nta qatorga qarab tanlaydi, aniq sozlama esa hech qachon "adashmaydi".
  • «Ko'pi oldin» = bo'sh saralash ([]). Statistika teglarni o'zi kamayish tartibida beradi. Saralashni olib tashlash — asl tartibga qaytish.

Tanlov va sarlavhalar bitta holatda:

tsx
function tanlovNomi(s: SortingState): string {
  if (s.length === 0) return "soni";
  if (s.length === 1 && s[0]?.id === "teg" && !s[0].desc)
    return "alifbo";
  return "ustun"; // sarlavha bosilgan
}

const ARIA_SORT = { asc: "ascending", desc: "descending" } as const;

export function TeglarJadvali({ teglar }: Props) {
  const [sorting, setSorting] = useState<SortingState>([]);
  const tanlovId = useId();
  const jadval = useTable({
    features: imkoniyatlar,
    columns: ustunlar,
    data: teglar,
    state: { sorting },
    onSortingChange: setSorting,
  });

Saralash holati — controlled (state + onSortingChange), chunki uni <select> ham o'qiydi va o'zgartiradi. tanlovNomi holatdan tanlov qiymatini hisoblaydi — ikkinchi state yo'q, ular hech qachon ajralib ketmaydi. Sarlavha bosilsa, tanlovda yashirin <option value="ustun" hidden>Ustun bo'yicha</option> ko'rinadi.

9.2 Jonli nusxa

Kanon komponentining JavaScript nusxasi — Tailwind sinflari va ulush chizig'isiz, mantiq aynan. Sarlavhalarni bosing, tanlovni o'zgartiring:

jsx
import { useEffect, useId, useState } from "react";
import {
  createColumnHelper,
  createSortedRowModel,
  rowSortingFeature,
  sortFn_basic,
  tableFeatures,
  useTable,
} from "@tanstack/react-table";
import { ArrowDown, ArrowUp, ArrowUpDown } from "lucide-react";

const imkoniyatlar = tableFeatures({
  rowSortingFeature,
  sortedRowModel: createSortedRowModel(),
  sortFns: { basic: sortFn_basic },
});
const ustun = createColumnHelper();
const ustunlar = ustun.columns([
  ustun.accessor("teg", { header: "Teg", sortFn: "basic" }),
  ustun.accessor("jami", {
    header: "Jami",
    sortFn: "basic",
    sortDescFirst: true,
  }),
  ustun.accessor("bajarildi", {
    header: "Bajarilgan",
    sortFn: "basic",
    sortDescFirst: true,
  }),
]);
const TANLOVLAR = { soni: [], alifbo: [{ id: "teg", desc: false }] };
function tanlovNomi(s) {
  if (s.length === 0) return "soni";
  if (s.length === 1 && s[0].id === "teg" && !s[0].desc) {
    return "alifbo";
  }
  return "ustun";
}
const ARIA_SORT = { asc: "ascending", desc: "descending" };
const TEGLAR = [
  { teg: "#bozor", jami: 2, bajarildi: 1 },
  { teg: "#aka", jami: 1, bajarildi: 0 },
  { teg: "#uy", jami: 1, bajarildi: 1 },
];

export default function App() {
  const [sorting, setSorting] = useState([]);
  const tanlovId = useId();
  const jadval = useTable({
    features: imkoniyatlar,
    columns: ustunlar,
    data: TEGLAR,
    state: { sorting },
    onSortingChange: setSorting,
  });
  useEffect(() => {
    const aria = [...document.querySelectorAll("th[scope=col]")].map(
      (th) => th.getAttribute("aria-sort") ?? "—",
    );
    console.log(tanlovNomi(sorting), aria.join(" "));
  }, [sorting]);
  return (
    <>
      <p>
        <label htmlFor={tanlovId}>Teglar tartibi</label>{" "}
        <select
          id={tanlovId}
          value={tanlovNomi(sorting)}
          onChange={(e) =>
            setSorting(
              e.target.value === "alifbo"
                ? TANLOVLAR.alifbo
                : TANLOVLAR.soni,
            )
          }
        >
          <option value="soni">Ko'pi oldin</option>
          <option value="alifbo">Alifbo bo'yicha</option>
          <option value="ustun" hidden>
            Ustun bo'yicha
          </option>
        </select>
      </p>
      <table>
        <caption>Teglar bo'yicha</caption>
        <thead>
          {jadval.getHeaderGroups().map((guruh) => (
            <tr key={guruh.id}>
              {guruh.headers.map((sarlavha) => {
                const tartib = sarlavha.column.getIsSorted();
                const Belgi =
                  tartib === "asc"
                    ? ArrowUp
                    : tartib === "desc"
                      ? ArrowDown
                      : ArrowUpDown;
                return (
                  <th
                    key={sarlavha.id}
                    scope="col"
                    aria-sort={
                      tartib === false ? undefined : ARIA_SORT[tartib]
                    }
                  >
                    <button
                      type="button"
                      onClick={sarlavha.column.getToggleSortingHandler()}
                    >
                      {/* kanonda: Tailwind sinflari */}
                      <jadval.FlexRender header={sarlavha} />
                      <Belgi size={14} />
                    </button>
                  </th>
                );
              })}
            </tr>
          ))}
        </thead>
        <tbody>
          {jadval.getRowModel().rows.map((qator) => {
            const t = qator.original;
            return (
              <tr key={t.teg}>
                <th scope="row">{t.teg}</th>
                <td>{t.jami}</td>
                <td>{t.bajarildi}</td>
              </tr>
            );
          })}
        </tbody>
      </table>
    </>
  );
}

Konsolda:

text
soni — — —
alifbo ascending — —
ustun — — descending
soni — — —

Ekranda:

text
Teglar tartibi Ko'pi oldin
Teglar bo'yicha
Teg Jami Bajarilgan
#bozor 2 1
#aka 1 0
#uy 1 1

Konsol — har o'zgarishdan keyin tanlov qiymati va uch sarlavhaning aria-sort i:

  1. Boshida — saralash yo'q, tanlov «Ko'pi oldin», aria-sort hech qayerda.
  2. «Teg» bosildi — tanlov o'zi «alifbo» ga o'tdi: sarlavha va tanlov bitta holatda.
  3. «Bajarilgan» — kamayishdan boshlandi (sortDescFirst), tanlov — «ustun». aria-sort faqat shu ustunda.
  4. Tanlovda «Ko'pi oldin» — saralash tozalandi, asl tartib qaytdi.

th lar scope bilan: sarlavha qatori — scope="col", qator boshidagi teg — scope="row". Malika katakda turib "#bozor, Bajarilgan, 1" ni eshitadi.

9.3 Tekshiruvlar

bash
git add -A
git commit

Commit (kanon): feat: teglar jadvali — TanStack Table v9, ustun bo'yicha saralash (aria-sort). Diff: 5 fayl, +242 −58 (shundan 74 qator — package-lock.json).

Tekshiruvlar (haqiqiy, holat 14):

  • npm test — node:test pass 198, Vitest Tests 65 passed (65); lint, format:check, tip, knip — toza. axe — 7 ko'rinishda 0 buzilish.
  • Build (tekshiruv-14.txt): birinchi yuklash JS o'zgarmadi — index-….js gzip 187.15 KB, holat 12 dagi bilan bir xil. TanStack Table statistika paneli bilan birga kech yuklanadi: BatafsilStatistika chunk'i 1.97 → 14.17 KB gzip (+12.2). Statistika panelini ochmagan foydalanuvchi bu kodni yuklamaydi (lazy va code splitting).
  • Brauzer (Chrome 155): 159, 72, 100 va 20-qism ssenariysiga beshta yangi tekshiruv:
text
✅ boshida: ko'pi oldin, aria-sort yo'q: {"teglar":["#bozor","#aka","#uy"],"aria":[null,null,null],"tanlov":"soni"}
✅ «Teg» bosildi: alifbo (o'sish), tanlov ham «alifbo»: {"teglar":["#aka","#bozor","#uy"],"aria":["ascending",null,null],"tanlov":"alifbo"}
✅ «Bajarilgan» bosildi: avval ko'pi (kamayish), tanlov «ustun»: {"teglar":["#bozor","#uy","#aka"],"aria":[null,null,"descending"],"tanlov":"ustun"}
✅ klaviatura: «Teg» — Enter: ["ascending",null,null]
✅ tanlov «Ko'pi oldin» — saralash olib tashlandi: {"teglar":["#bozor","#aka","#uy"],"aria":[null,null,null],"tanlov":"soni"}

To'rtinchi qator — Bobur yo'li: Tab bilan «Teg» tugmasiga kelib, Enter. Jonli nusxadagi tartiblar shu e2e bilan bir xil.

Virtual ro'yxat (18-qism) bu qadamda o'zgarmadi: vazifalar ro'yxati jadval emas, teglar esa o'nlab — virtualizatsiya kerak emas.

10. Ko'p uchraydigan xatolar

Belgi Sabab Davosi
saralash API'si yo'q (… is not a function) imkoniyat yoki qator modeli tableFeatures da yo'q rowSortingFeature + sortedRowModel: createSortedRowModel()
sortFn 'text' (auto) … is not registered "auto" tanlagan nom ro'yxatda yo'q sortFns ga qo'shing yoki ustunda sortFn
jadval har renderda qayta hisoblaydi, sekin columns/data/features render ichida yaratilgan modul darajasida yoki useMemo; bo'sh massiv — doimiy
sarlavha bosiladi, tartib qaytib ketadi onSortingChange bor, state.sorting yo'q ikkalasi juft
Bobur saralay olmaydi onClick th da th ichida <button>
filtrdan keyin bo'sh sahifa qo'lda sahifalash, tiklash yo'q jadval sahifalashi (autoResetPageIndex)
telefon apostrofi bilan «ko'k» «Ko'k choy» ni topmaydi includesString apostroflarni farqlaydi o'z filterFn — normallash bilan
v8 maqoladagi kod useReactTable topmaydi v9 o'rnatilgan useTable + tableFeatures (olti qadam)
table.getState is not a function v9 da yo'q table.state

11. Mashqlar

Mashqlar kurs/mashqlar/20/14-tanstack-table/ da. Har yechimni «Ishga tushir» bilan sinang.

1-mashq (oson): v8 → v9

Eski koddagi nomning v9 dagi o'rnini yozing:

  • useReactTable(…) →
  • getSortedRowModel: getSortedRowModel() → sortedRowModel:
  • table.getState().sorting →
  • ustundagi sortingFn: "alphanumeric" →
Yechim
  • useTable — va komponentdan tashqarida features = tableFeatures({ … }).
  • createSortedRowModel() — tableFeatures ichida, rowSortingFeature dan keyin.
  • table.state.sorting — v9 da getState() yo'q.
  • sortFn: "alphanumeric" — va sortFns: { alphanumeric: sortFn_alphanumeric } ro'yxatga.

2-mashq (o'rta): turkum bo'yicha ustun filtri

Menyu jadvaliga «Turkum» tanlovi qo'shing: «Hammasi», «Taom», «Ichimlik». Tanlanganda faqat shu turkumdagi qatorlar qolsin. Ostida role="status" — «Topildi: N».

Ishora: «Ustun filtri» bo'limi. Imkoniyat — columnFilteringFeature, model — filteredRowModel, funksiya — filterFn_equalsString (filterFns ga equalsString nomi bilan), ustunda filterFn: "equalsString". Qiymatni berish: table.getColumn("category").setFilterValue(...). «Hammasi» uchun undefined bering — filtr olib tashlanadi.

Yechim
jsx
import {
  columnFilteringFeature,
  createColumnHelper,
  createFilteredRowModel,
  filterFn_equalsString,
  tableFeatures,
  useTable,
} from "@tanstack/react-table";

const features = tableFeatures({
  columnFilteringFeature,
  filteredRowModel: createFilteredRowModel(),
  filterFns: { equalsString: filterFn_equalsString },
});
const col = createColumnHelper();
const columns = col.columns([
  col.accessor("name", { header: "Nomi" }),
  col.accessor("category", {
    header: "Turkum",
    filterFn: "equalsString",
  }),
]);
const data = [
  { name: "Osh", category: "taom" },
  { name: "Ko'k choy", category: "ichimlik" },
  { name: "Manti", category: "taom" },
  { name: "Ayron", category: "ichimlik" },
];

export default function App() {
  const table = useTable({ features, columns, data });
  const category = table.getColumn("category");
  const rows = table.getRowModel().rows;
  return (
    <div>
      <label>
        Turkum{" "}
        <select
          value={category.getFilterValue() ?? ""}
          onChange={(e) =>
            category.setFilterValue(e.target.value || undefined)
          }
        >
          <option value="">Hammasi</option>
          <option value="taom">Taom</option>
          <option value="ichimlik">Ichimlik</option>
        </select>
      </label>
      <p role="status">Topildi: {rows.length}</p>
      <ul>
        {rows.map((row) => (
          <li key={row.id}>{row.original.name}</li>
        ))}
      </ul>
    </div>
  );
}

Ekranda:

text
Turkum Ichimlik
Topildi: 2
Ko'k choy
Ayron

setFilterValue(undefined) — filtrni holatdan butunlay olib tashlaydi (columnFilters dan shu ustun yo'qoladi). equalsString katta-kichik harfni farqlamaydi: «Ichimlik» va «ichimlik» bir xil.

3-mashq (qiyin): sahifa hajmi va "3–4 / 5"

«Sahifalash» bo'limidagi jadvalga qo'shing:

  1. «Sahifada» tanlovi: 2 yoki 3 qator.
  2. Pastda: «Ko'rsatilmoqda: 3–4 / 5» — qaysi qatorlar ko'rinib turibdi va jami nechta.
  3. Konsolga har sahifa o'zgarganda: sahifa: N, hajm: M.

Ishora: table.setPageSize(Number(value)). Oraliq: birinchi qator — pageIndex * pageSize + 1, oxirgisi — Math.min bilan (oxirgi sahifa to'liq bo'lmasligi mumkin). Jami — table.getFilteredRowModel().rows.length.

Yechim
jsx
import { useEffect } from "react";
import {
  createColumnHelper,
  createPaginatedRowModel,
  rowPaginationFeature,
  tableFeatures,
  useTable,
} from "@tanstack/react-table";

const features = tableFeatures({
  rowPaginationFeature,
  paginatedRowModel: createPaginatedRowModel(),
});
const col = createColumnHelper();
const columns = col.columns([
  col.accessor("name", { header: "Taom" }),
]);
const data = ["Osh", "Manti", "Lag'mon", "Somsa", "Ko'k choy"].map(
  (name) => ({ name }),
);

export default function App() {
  const table = useTable({
    features,
    columns,
    data,
    initialState: { pagination: { pageIndex: 0, pageSize: 2 } },
  });
  const { pageIndex, pageSize } = table.state.pagination;
  const total = table.getFilteredRowModel().rows.length;
  const from = pageIndex * pageSize + 1;
  const to = Math.min(from + pageSize - 1, total);
  useEffect(() => {
    console.log(`sahifa: ${pageIndex + 1}, hajm: ${pageSize}`);
  }, [pageIndex, pageSize]);
  return (
    <div>
      <ul>
        {table.getRowModel().rows.map((row) => (
          <li key={row.id}>{row.original.name}</li>
        ))}
      </ul>
      <p>
        Ko'rsatilmoqda: {from}–{to} / {total}
      </p>
      <button
        onClick={() => table.nextPage()}
        disabled={!table.getCanNextPage()}
      >
        Keyingi
      </button>{" "}
      <label>
        Sahifada{" "}
        <select
          value={pageSize}
          onChange={(e) => table.setPageSize(Number(e.target.value))}
        >
          <option value="2">2</option>
          <option value="3">3</option>
        </select>
      </label>
    </div>
  );
}

Konsolda:

text
sahifa: 1, hajm: 2
sahifa: 2, hajm: 2
sahifa: 1, hajm: 3

Ekranda:

text
Osh
Manti
Lag'mon
Ko'rsatilmoqda: 1–3 / 5
Keyingi Sahifada 3

Uchinchi qatorga qarang: 2-sahifada (3–4-qatorlar) turib hajmni 3 ga o'zgartirdik — jadval 1-sahifaga o'tdi. Sabab: setPageSize foydalanuvchi ko'rib turgan birinchi qatorni saqlashga harakat qiladi. U 3-qator (indeks 2) edi: Math.floor(2 / 3) = 0 — yangi hajmda bu qator 1-sahifada. Shuning uchun «Lag'mon» ekranda qoldi.

4-mashq: Vazifalar qadami

feat/teglar-jadvali branch'ida: npm i -E @tanstack/react-table@9.2.6, yangi TeglarJadvali.tsx (imkoniyatlar, ustunlar, TANLOVLAR, tanlovNomi, useTable controlled saralash bilan, sarlavha tugmalari va aria-sort), BatafsilStatistika.tsx da eski jadval o'rniga <TeglarJadvali teglar={s.teglar} />, batafsil-statistika.ts da TegHisobi — export type. Keyin commit.

Yechim

Tekshiruv ro'yxati:

  • Statistika panelini oching: «Teg» ni bosing — teglar alifbo tartibida, tanlovda «Alifbo bo'yicha». «Jami» — avval ko'pi.
  • DevTools Elements: saralangan ustunning th ida aria-sort, boshqalarida yo'q.
  • Klaviatura: Tab bilan «Teg» tugmasiga — Enter saralaydi.
  • npm run build: index-….js gzip o'zgarmagan, BatafsilStatistika-….js ~14 KB. npm test — node 198, Vitest 65; npm run tip — 0 xato.

12. Real ishda

  • Admin panellar — buyurtmalar, mijozlar, mahsulotlar, hisobotlar: deyarli har biri saralash, filtr va sahifali jadval. TanStack Table — React'da headless jadvalning eng ko'p ishlatiladigan yo'li; shadcn DataTable ham uning ustida.
  • Kompaniyaning o'z dizayn tizimi bo'lsa, headless aynan kerak: jadval mantiqi bitta, ko'rinish — kompaniyaniki. Tayyor jadval kerak bo'lsa — AG Grid, MUI DataGrid (keyingi dars).
  • Migratsiya ishi — ko'p loyihalar hali v8 da. "v8 → v9" vazifasi real va olti qadamli ro'yxat bilan bajariladi.
  • Intervyu savollari: "Headless jadval nima?", "Filtr, saralash va sahifalash qaysi tartibda qo'llanadi va nega?", "aria-sort qayerga qo'yiladi?", "Jadvalga columns ni nega useMemo bilan berasiz?", "Controlled va uncontrolled saralash farqi?".

Xulosa

  • TanStack Table — headless: qatorlarni filtrlaydi, saralaydi, sahifalaydi; <table>, uslub va a11y — sizniki.
  • v9: useTable({ features, columns, data }), features = tableFeatures({ imkoniyat, …RowModel: create…RowModel(), sortFns/filterFns }) — faqat keraklisi. v8 (useReactTable, get…RowModel()) — eski kodda.
  • features, columns, data — barqaror havolalar (modul darajasi, useMemo, doimiy bo'sh massiv).
  • Saralash: getToggleSortingHandler, sonlar avval kamayish, th ichida tugma, aria-sort faqat saralangan ustunda; "auto" nomini ro'yxatga qo'shing.
  • Filtr: globalFilter, ustun filtri, o'z filterFn (o'zbekcha apostroflar); sahifalash: filtr o'zgarsa pageIndex 0 ga qaytadi.
  • Holat: jadvalda (initialState) yoki sizda (state + on…Change, juft).

Keyingi dars: Jadvallar II: server tomonli jadval va katta ma'lumot — manualPagination va manualSorting bilan serverda sahifalash, holatni URL'da saqlash, qator tanlash, ustun ko'rinishi va o'lchami, virtualizatsiya; AG Grid va MUI DataGrid.

Manbalar

  • TanStack Table v9: Overview, Migrating to v9, Sorting, Column Filtering, Global Filtering, Pagination — tanstack.com/table (9.2.6); paket ichidagi skills/ hujjatlari
  • WAI-ARIA APG: Sortable Table example, aria-sort — w3.org/WAI/ARIA/apg
  • shadcn/ui: Data Table — ui.shadcn.com/docs/components/data-table (v8 bilan)
  • MDN: <table>, <caption>, scope atributi — developer.mozilla.org
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
TanStack Table asoslari (Jadvallar I): useTable, ustunlar, saralash, filtr va sahifalash — IlmHamroh