মূল ধারণা — useReactTable ও row/cell/column
useReactTable-এর option, table/row/cell/column ইনস্ট্যান্স, flexRender-এর context, controlled বনাম uncontrolled state — বাংলায়।
আগের অধ্যায়ে একটা কাজ করা টেবিল দেখলাম। এখন সেই কোডের পিছনের যুক্তিটা বোঝা যাক — কেন getCoreRowModel লাগে, row জিনিসটা আসলে কী, আর flexRender-এর ভেতরে কী পাওয়া যায়।
useReactTable — একটি কেন্দ্রীয় hook
const table = useReactTable({
// ১. কী দেখাবো
data,
columns,
// ২. এখন কোন অবস্থায় আছে (controlled)
state: { sorting, pagination },
onSortingChange: setSorting,
onPaginationChange: setPagination,
// ৩. শুরুর অবস্থা (uncontrolled)
initialState: { pagination: { pageSize: 10 } },
// ৪. কীভাবে transform হবে
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
// ৫. কোনো কাজ কি আমি নিজে করব
manualSorting: false,
});এই একটি hook থেকে যা পাওয়া যায় তা table — এর উপর রয়েছে সব state ও action। এটা একটা state machine-এর মতো: অবস্থা বদলালে React নিজে থেকেই নতুন করে হিসাব করে।
table-এর উপর যা আছে
// state পড়া
table.getState() // { sorting, pagination, globalFilter, ... }
table.getState().sorting // SortingState
// একটি row model-এর ফলাখানা
table.getRowModel().rows // ← রেন্ডারে এটাই ব্যবহার হয়
table.getCoreRowModel().rows // raw data থেকে তৈরি মূল row
table.getSortedRowModel().rows // সাজানো row
table.getSelectedRowModel().rows
// কলাম খুঁজে বের করা
const priceCol = table.getColumn("price");
priceCol.getIsVisible()
// column visibility
table.getAllLeafColumns()
table.getVisibleLeafColumns()
// পেজ সংক্রান্ত
table.getCanNextPage()
table.getPageCount()নিয়মটা সহজ: যা পড়তে চান সব get*, যা বদলাতে চান সব set*/toggle*, আর যা DOM-এর সাথে জুড়ে দেওয়ার কাজ (event handler) তার নাম get*Handler() দিয়ে শুরু হয়।
Row ইনস্ট্যান্স — একটি row-এর সব তথ্য
table.getRowModel().rows.map((row) => {
row.id // "p-01" (getRowId দিলে) বা index "0"
row.index // মূল ডেটার index
row.original // ← আপনার মূল Product অবজেক্ট
row.getVisibleCells() // লুকানো কলাম বাদ দিয়ে cell
row.getAllCells() // সব cell
row.getCell("price") // নির্দিষ্ট কলামের cell
// sub-row (row expanding)
row.getLeafRows()
row.getParentRow()
row.subRows
row.getCanExpand()
return null;
})}row.original হলো আপনার ডেটা। বাকি সব TanStack-এর হিসাব করা অবজেক্ট। কোনো সময় row.original.name লিখতে ভুলে row.name লিখে ফেললে সেটা ভুল লাগবে — row এর নিজস্ব property নেই, এগুলো get* method।
দুটোই কাজ করে, কিন্তু row.original দ্রুত ও টাইপ-সেফ — row টাইপ Product, তাই original-এর property ভুল লিখলে কম্পাইলার ধরে। row.getValue("price") লিখলে কিন্তু price স্ট্রিং হিসেবে গোনে, তাই + 1 করলে concat হয়ে যায়। সংখ্যা নিয়ে কাজ করলে getValue ব্যবহার করে Number(...) করুন।
Cell ও Column ইনস্ট্যান্স
const cell = row.getCell("price");
cell.id // `${row.id}_price` — key হিসেবে ব্যবহার্য
cell.column // ← এই cell-এর ColumnDef
cell.row // ← কোন row-এ পড়ছে
cell.getValue() // মান (accessor বা cell renderer ছাড়া)
cell.renderValue() // মান render করে (JSX)
cell.getIsPlaceholder()
cell.getContext() // ← flexRender-এ যা যায়const column = table.getColumn("price");
column.id
column.columnDef // ← আপনার লেখা ColumnDef অবজেক্ট
column.getIsVisible()
column.getCanHide()
column.getCanSort()
column.getIsSorted()
column.getSortDirection()
column.getToggleSortingHandler()
column.getFacetedUniqueValues() // filter-এর জন্য অপশন তালিকা
column.getFacetedRowModel()getContext()-এ যা থাকে: table, column, row, cell, staticRowIndex, staticColumnIndex। মানে cell: ({ row, table, getValue }) => … — এই সব parameter এখান থেকেই আসে।
flexRender — header ও cell রেন্ডারের সেতু
ColumnDef-এ header ও cell যেকোনো একটা ফর্মে লেখা যায় — string, function, বা component। flexRender সেটা ঠিকঠাক context দিয়ে render করে।
import { flexRender } from "@tanstack/react-table";
// columnDef: { header: "মূল্য" } → string
// columnDef: { header: ({ column }) => … } → function
// columnDef: { cell: PriceCell } → component
flexRender(header.column.columnDef.header, header.getContext());| লেখার ধরন | উদাহরণ | কখন ব্যবহার |
|---|---|---|
| string | header: "নাম" | শুধু লেখা দেখাতে |
| function | cell: ({ row }) => row.original.name | ডেটা থেকে কিছু বানাতে |
| ReactNode | header: <span>মূল্য</span> | markup দরকার হলে |
| component ref | cell: PriceCell | পুনঃব্যবহারযোগ্য UI |
function-এর getValue() ব্যবহার করা ভালো — কারণ accessorKey-র নাম বদলালেও কোড ভাঙে না, আর টাইপও ইনফার করা যায়।
controlled বনাম uncontrolled state
এটা React-এর পরিচিত নিয়ম, TanStack-এও হুবহু একই।
// Uncontrolled — table নিজের মধ্যে state ধরে রাখে
useReactTable({
columns,
data,
getCoreRowModel: getCoreRowModel(),
initialState: { pagination: { pageSize: 20 } },
})
// Controlled — state আমাদের কাছে, আমরাই বদলাই
const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: 10 });
useReactTable({
columns,
data,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
state: { pagination }, // ← আমরা দিচ্ছি
onPaginationChange: setPagination, // ← আমরাই বদলাবো
})| Uncontrolled | Controlled | |
|---|---|---|
| কে ধরে রাখে | table নিজে | আপনি |
| কীভাবে শুরু | initialState | useState |
| কীভাবে বদলে | table নিজে | on*Change handler |
| URL-এ দেখানো যায় | না | হ্যাঁ |
| Query-এর সাথে যুক্ত | কঠিন | সহজ |
প্রায় সব real অ্যাপে controlled লাগবে — কারণ sorting/filtering/pagination অবস্থা server-এর query param হিসেবেও যেতে হয় (অধ্যায় ১১)। তবে columnVisibility, rowSelection-এর মতো UI-only state uncontrolled রাখলেও চলে।
ছোট একটা পরীক্ষা
const table = useReactTable({
data: mockProducts,
columns,
getCoreRowModel: getCoreRowModel(),
});
// DevTools-এ বা console-এ লিখে দেখুন
console.log(table.getRowModel().rows.length); // ১২
console.log(table.getAllLeafColumns().map((c) => c.id)); // ["name","price"]
console.log(table.getRowModel().rows[0].original); // প্রথম Product অবজেক্টgetRowId: (row) => row.id না দিলে row.id মানে হয় row-এর index — "0", "1", "2"। ডেটা সাজালে বা filter বদলালে index বদলে যায়, ফলে selection ভেঙে যায়। getRowId কেন এত জরুরি, তা অধ্যায় ৫ ও ৯-এ বিস্তারিত।
সারসংক্ষেপ
useReactTable= একটি hook →tableinstancetable.getRowModel()রেন্ডারের জন্য; বাকিget*RowModel()debug/হিসাবেrow.originalআপনার ডেটা, বাকি সব computedflexRender(def, context)= যেকোনো ধরনের header/cell render করার একটাই উপায়- state controlled রাখলে URL ও Query-এর সাথে জোড়া লাগানো সহজ
পরের অধ্যায়ে columns নিয়ে বিস্তারিত — accessorKey, accessorFn, id, cell, meta এবং একটা কলামের সব option।