Mundarija (32)
- Bu darsda
- 1. Nega bu kerak?
- 2. Headless jadval qanday ishlaydi
- 2.1 Qator modellari zanjiri
- 2.2 Versiya: v9 va "eski kodda ko'rsangiz"
- 3. Birinchi jadval
- 3.1 Ustunlar va ma'lumot
- 3.2 Barqaror kirishlar
- 4. Saralash
- 4.1 Imkoniyatni ulash
- 4.2 aria-sort va klaviatura
- 4.3 Tuzoq: ro'yxatdan o'tmagan sortFn
- 5. Filtr va qidiruv
- 5.1 Global filtr
- 5.2 O'zbekcha apostrof: o'z filtr funksiyangiz
- 5.3 Ustun filtri
- 6. Sahifalash
- 7. Holat kimda: jadvalda yoki sizda
- 8. Eski kodda ko'rsangiz: v8 va shadcn DataTable
- 9. Vazifalar qadami: teglar jadvali (holat 14)
- 9.1 Nima o'zgaradi
- 9.2 Jonli nusxa
- 9.3 Tekshiruvlar
- 10. Ko'p uchraydigan xatolar
- 11. Mashqlar
- 1-mashq (oson): v8 → v9
- 2-mashq (o'rta): turkum bo'yicha ustun filtri
- 3-mashq (qiyin): sahifa hajmi va "3–4 / 5"
- 4-mashq: Vazifalar qadami
- 12. Real ishda
- Xulosa
- Manbalar
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) jadvaluseTable({ features, columns, data })bilan yaratiladi:features—tableFeatures({...})ichida faqat kerakli imkoniyatlar (saralash, filtr, sahifalash) va ularning qator modellari. UstunlarcreateColumnHelperbilan yoziladi, ekrangatable.getRowModel().rowschiziladi. Eski maqolalar va shadcn DataTable'dagiuseReactTable,getCoreRowModel()— v8 API'si.
Bu darsda
- Headless jadval nima ekanini va TanStack Table aynan nimani o'z zimmasiga olishini tushuntirasiz.
tableFeatures,createColumnHelpervauseTablebilan birinchi jadvalni chizasiz.- Sarlavhani bosib saralashni,
aria-sortni 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:
- 3-sahifada turganda qidiruvga yozdi — natija 1 sahifalik, ekran esa bo'sh: sahifa raqami 3 da qolgan.
- Saralash avval sahifani kesib, keyin saraladi — "eng qimmat" faqat birinchi o'ntaning ichida qidirildi.
- 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:
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:
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 qatordaginamemaydonidan 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). Ungaidmajburiy.getHeaderGroups()— sarlavha qatorlari. Odatda bitta qator; ustunlar guruhlansa ("Narx" ostida "Eski" va "Yangi") — bir nechta.table.FlexRender—headeryokicellning 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():
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:
sorting: []
sorting: [{"id":"price","desc":true}]
sorting: [{"id":"price","desc":false}]
sorting: [{"id":"name","desc":false}]Ekranda:
Taom Narx
Ko'k choy 5000
Manti 30000
Osh 35000Konsol — saralash holati (state): massiv, har elementi { id, desc }. Uni o'qiymiz:
- «Narx» birinchi bosish —
desc: true, ya'ni kamayish: eng qimmat tepada. Sonli ustun avval kamayish bilan boshlanadi — jadvallarda odatda "eng katta" qidiriladi. - Ikkinchi bosish — o'sish.
- «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
thichida.thning o'zi fokus olmaydi.onClicknithga qo'ysangiz, Bobur (faqat klaviatura) saralay olmaydi.<button>— Tab bilan keladi, Enter va Space bilan bosiladi. aria-sort—thda, tugmada emas. Qiymatiascendingyokidescending. 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 ustundaaria-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:
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:
sortFn 'text' (auto) for column 'name' is not registeredEkranda:
Taom
Osh
mantiTarjimasi: "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:
sortFnsgatext: sortFn_textqo'shish, yoki- ustunda aniq aytish:
col.accessor("name", { header: "Taom", sortFn: "text" }). Kanonda esasortFn: "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.
basicbilan 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:
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:
Topildi: 1
MantiincludesString 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:
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:
Telefondan yozish
includesString: topilmadi
uz: Ko'k choyBir 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):
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:
Narx
Osh — 35000
Lag'mon — 28000
Oldingi 1 / 2 (jami 4) KeyingiQadamma-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:
// 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:
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:
useReactTable→useTable; komponentdan tashqaridafeatures.getCoreRowModel— o'chiriladi (avtomatik).- Har
get…RowModel: get…RowModel()→tableFeaturesichida imkoniyat +…RowModel: create…RowModel(). Imkoniyat oldin yoziladi. sortingFns→sortFns,sortingFn(ustunda) →sortFn; built-in funksiyalarni alohida import qilib ro'yxatga qo'shing.table.getState()→table.state.- 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.
git switch -c feat/teglar-jadvali
npm i -E @tanstack/react-table@9.2.6Jadval alohida faylga ko'chdi — xususiyatlar/statistika/TeglarJadvali.tsx (162 qator). Asosiy qismlari (kanondan):
// 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",textemas. Teglarnormalla()dan o'tgan — hammasi kichik harf. 18-qismdagi "Alifbo bo'yicha" ham<bilan solishtirardi (Intl.Collatoremas: Node va Chrome'da o'zbekcha qoidalar har xil).basicaynan 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:
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:
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:
soni — — —
alifbo ascending — —
ustun — — descending
soni — — —Ekranda:
Teglar tartibi Ko'pi oldin
Teglar bo'yicha
Teg Jami Bajarilgan
#bozor 2 1
#aka 1 0
#uy 1 1Konsol — har o'zgarishdan keyin tanlov qiymati va uch sarlavhaning aria-sort i:
- Boshida — saralash yo'q, tanlov «Ko'pi oldin»,
aria-sorthech qayerda. - «Teg» bosildi — tanlov o'zi «alifbo» ga o'tdi: sarlavha va tanlov bitta holatda.
- «Bajarilgan» — kamayishdan boshlandi (
sortDescFirst), tanlov — «ustun».aria-sortfaqat shu ustunda. - 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
git add -A
git commitCommit (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:testpass 198, VitestTests 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-….jsgzip 187.15 KB, holat 12 dagi bilan bir xil. TanStack Table statistika paneli bilan birga kech yuklanadi:BatafsilStatistikachunk'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:
✅ 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 tashqaridafeatures = tableFeatures({ … }).createSortedRowModel()—tableFeaturesichida,rowSortingFeaturedan keyin.table.state.sorting— v9 dagetState()yo'q.sortFn: "alphanumeric"— vasortFns: { 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
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:
Turkum Ichimlik
Topildi: 2
Ko'k choy
AyronsetFilterValue(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:
- «Sahifada» tanlovi: 2 yoki 3 qator.
- Pastda: «Ko'rsatilmoqda: 3–4 / 5» — qaysi qatorlar ko'rinib turibdi va jami nechta.
- 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
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:
sahifa: 1, hajm: 2
sahifa: 2, hajm: 2
sahifa: 1, hajm: 3Ekranda:
Osh
Manti
Lag'mon
Ko'rsatilmoqda: 1–3 / 5
Keyingi Sahifada 3Uchinchi 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
thidaaria-sort, boshqalarida yo'q. - Klaviatura: Tab bilan «Teg» tugmasiga — Enter saralaydi.
npm run build:index-….jsgzip 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-sortqayerga qo'yiladi?", "Jadvalgacolumnsni negauseMemobilan 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,thichida tugma,aria-sortfaqat saralangan ustunda; "auto" nomini ro'yxatga qo'shing. - Filtr:
globalFilter, ustun filtri, o'zfilterFn(o'zbekcha apostroflar); sahifalash: filtr o'zgarsapageIndex0 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>,scopeatributi — developer.mozilla.org
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!