:基于 @refinedev/react-table 的 useTable 分頁實現(xiàn)與原理)
Refine TanStack Table 分頁實戰(zhàn)基于 refinedev/react-table 的 useTable 分頁實現(xiàn)與原理【免費下載鏈接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.項目地址: https://gitcode.com/GitHub_Trending/re/refine本指南以 Refine 官方文檔中useTable分頁章節(jié)的可運行示例_partial-pagination-live-preview.md為骨架完整講解如何用refinedev/react-table在 Refine v5 項目中實現(xiàn)服務(wù)端/客戶端分頁表格從安裝、完整可復(fù)制的分頁 UI 代碼到setPageIndex、setPageSize、getPrePaginationRowModel等 TanStack Table API 的逐一拆解并結(jié)合refinedev/react-table的源碼packages/react-table/src/useTable/index.ts與測試用例packages/react-table/src/useTable/index.spec.ts講清底層工作原理。讀完你可以獨立搭建一個帶頁碼跳轉(zhuǎn)、上一頁/下一頁、每頁條數(shù)切換的 headless 分頁表格。背景headless 的 TanStack Table 適配器Refine 官方為 TanStack Table 中明確說明所有 TanStack Table 的特性都被支持官方示例可以直接復(fù)制粘貼到項目中使用。useTable是從refinedev/core包中的useTable擴展而來底層通過useList獲取數(shù)據(jù)。也就是說你既能使用 coreuseTable的全部能力資源解析、數(shù)據(jù)提供器對接、syncWithLocation、實時更新等也能使用 TanStack Table 的全部表格能力。安裝使用你習(xí)慣的包管理器安裝適配包npm install refinedev/react-table # 或 pnpm add refinedev/react-table # 或 yarn add refinedev/react-table該包內(nèi)部依賴tanstack/react-tableRefine 會將其作為依賴一并安裝無需手動安裝 TanStack Table 本身。完整示例一個帶分頁控制的帖子列表下面這段代碼完整來自官方分頁示例見 documentation/docs/packages/tanstack-table/examples/_partial-pagination-live-preview.md。它渲染一個posts資源列表并在表格下方構(gòu)建了一個完全自繪的分頁條首頁/上一頁/下一頁/末頁四個按鈕、當(dāng)前頁碼與總頁數(shù)、跳頁輸入框、每頁條數(shù)下拉選擇以及總行數(shù)統(tǒng)計。import React from react; import { useTable } from refinedev/react-table; import { ColumnDef, flexRender } from tanstack/react-table; interface IPost { id: number; title: string; content: string; status: published | draft | rejected; } const PostList: React.FC () { const columns React.useMemoColumnDefIPost[]( () [ { id: id, header: ID, accessorKey: id, }, { id: title, header: Title, accessorKey: title, }, { id: status, header: Status, accessorKey: status, }, { id: createdAt, header: CreatedAt, accessorKey: createdAt, }, ], [], ); const { reactTable: { getHeaderGroups, getRowModel, // 分頁相關(guān) API getState, setPageIndex, getCanPreviousPage, getPageCount, getCanNextPage, nextPage, previousPage, setPageSize, getPrePaginationRowModel, }, } useTable({ columns, }); return ( div table thead {getHeaderGroups().map((headerGroup) ( tr key{headerGroup.id} {headerGroup.headers.map((header) { return ( th key{header.id} {header.isPlaceholder ? null : ( {flexRender( header.column.columnDef.header, header.getContext(), )} / )} /th ); })} /tr ))} /thead tbody {getRowModel().rows.map((row) { return ( tr key{row.id} {row.getVisibleCells().map((cell) { return ( td key{cell.id} {flexRender( cell.column.columnDef.cell, cell.getContext(), )} /td ); })} /tr ); })} /tbody /table {/* 分頁 UI 可以任意自繪這里只是最基礎(chǔ)的實現(xiàn) */} div button onClick{() setPageIndex(0)} disabled{!getCanPreviousPage()} {} /button button onClick{() previousPage()} disabled{!getCanPreviousPage()} {} /button button onClick{() nextPage()} disabled{!getCanNextPage()} {} /button button onClick{() setPageIndex(getPageCount() - 1)} disabled{!getCanNextPage()} {} /button span divPage/div strong {getState().pagination.pageIndex 1} of {getPageCount()} /strong /span span | Go to page: input typenumber defaultValue{getState().pagination.pageIndex 1} onChange{(e) { const page e.target.value ? Number(e.target.value) - 1 : 0; setPageIndex(page); }} / /span select value{getState().pagination.pageSize} onChange{(e) { setPageSize(Number(e.target.value)); }} {[10, 20, 30, 40, 50].map((pageSize) ( option key{pageSize} value{pageSize} Show {pageSize} /option ))} /select /div div{getPrePaginationRowModel().rows.length} Rows/div /div ); };把PostList掛到路由上并確保Refine中注冊了posts資源與對應(yīng)數(shù)據(jù)提供器即可運行。分頁 API 逐一拆解示例中從useTable()返回的reactTable對象里解構(gòu)出來的 9 個分頁相關(guān) API都是 TanStack Table v8 的標(biāo)準(zhǔn)接口API作用示例中的用法getState()讀取當(dāng)前表格狀態(tài)含pagination.pageIndex、pagination.pageSizegetState().pagination.pageIndex 1顯示當(dāng)前頁TanStack 的pageIndex從 0 開始顯示時 1setPageIndex(index)跳轉(zhuǎn)到指定頁0 基索引首頁按鈕setPageIndex(0)末頁按鈕setPageIndex(getPageCount() - 1)跳頁輸入框把用戶輸入的 1 基頁碼減 1 后傳入getCanPreviousPage()是否存在上一頁即是否在第一頁作為、按鈕的disabled條件getPageCount()總頁數(shù)末頁按鈕與Page x of y展示getCanNextPage()是否存在下一頁即是否在末頁作為、按鈕的disabled條件nextPage()下一頁按鈕點擊事件previousPage()上一頁按鈕點擊事件setPageSize(size)設(shè)置每頁條數(shù)select的onChange取值來自[10, 20, 30, 40, 50]getPrePaginationRowModel()返回分頁前的行模型全量行g(shù)etPrePaginationRowModel().rows.length展示總行數(shù)兩點關(guān)鍵提示pageIndex是 0 基、currentPage是 1 基TanStack Table 內(nèi)部pageIndex從 0 開始計數(shù)因此顯示頁碼要1跳頁輸入框要把用戶輸入減 1。跳頁輸入的邊界處理const page e.target.value ? Number(e.target.value) - 1 : 0;—— 輸入為空時回退到第 0 頁第一頁避免NaN傳入。底層原理useTable 是如何把分頁與 Refine 數(shù)據(jù)請求聯(lián)動起來的官方分頁章節(jié)文檔指出「setPageSize等方法的每次變更都會觸發(fā)一次新的數(shù)據(jù)提供器請求」。要理解這一點需要看 packages/react-table/src/useTable/index.ts 的實現(xiàn)const isPaginationEnabled refineCoreProps.pagination?.mode ! off; // ... const reactTableResult useReactTableTData({ data: data?.data ?? [], // ... initialState: { pagination: { pageIndex: currentPage - 1, pageSize: pageSizeCore, }, // ... }, pageCount, manualPagination: true, // ... });關(guān)鍵機制有三條manualPagination: true手動分頁TanStack Table 不再在客戶端自動切片data而是把分頁狀態(tài)完全交給開發(fā)者/外層框架控制配合 Refine 的服務(wù)端數(shù)據(jù)獲取。初始狀態(tài)雙向映射coreuseTable的currentPage1 基映射為 TanStack 的pageIndex0 基即currentPage - 1pageSize直接透傳。同時把 core 返回的pageCount作為 TanStack 的總頁數(shù)來源。狀態(tài)同步useEffect 反向聯(lián)動當(dāng)用戶在 UI 上調(diào)用setPageIndex/setPageSize改變 TanStack 狀態(tài)后兩個useEffect會把新的pageIndex 1與pageSize寫回 core 的setCurrentPage/setPageSizeCore從而觸發(fā)useList重新請求數(shù)據(jù)useEffect(() { if (pageIndex ! undefined) { setCurrentPage(pageIndex 1); } }, [pageIndex]); useEffect(() { if (pageSize ! undefined) { setPageSizeCore(pageSize); } }, [pageSize]);測試用例印證了這一行為packages/react-table/src/useTable/index.spec.ts默認(rèn)無參數(shù)時state.pagination?.pageIndex為0、pageSize為10、pageCount為1傳入pagination: { currentPage: 2, pageSize: 1 }后core 側(cè)currentPage為2、pageSize為1TanStack 側(cè)pageIndex為1、pageSize為1pageCount變?yōu)?。配置項pagination.currentPage / pageSize / mode分頁行為通過useTable的refineCoreProps.pagination配置完整參數(shù)表見 useTable 官方文檔useTable({ refineCoreProps: { pagination: { currentPage: 2, // 初始頁碼1 基默認(rèn) 1 pageSize: 10, // 初始每頁條數(shù)默認(rèn) 10 mode: server, // server | client | off默認(rèn) server }, }, });參數(shù)類型默認(rèn)值說明pagination.currentPagenumber1初始頁碼1 基pagination.pageSizenumber10初始每頁條數(shù)pagination.modeoff \| server \| clientserver分頁模式見下三種mode的含義server默認(rèn)服務(wù)端分頁。數(shù)據(jù)提供器根據(jù)currentPage與pageSize請求對應(yīng)頁的數(shù)據(jù)TanStack Table 通過manualPagination: true不做客戶端切片。這也是示例中g(shù)etPrePaginationRowModel().rows.length能拿到總數(shù)的原因——總行數(shù)來自服務(wù)端返回的total。client客戶端分頁。一次性拉取全量記錄由 TanStack Table 在客戶端完成切片。適用于數(shù)據(jù)量較小的場景。off關(guān)閉分頁一次請求拉取全部記錄。從源碼看mode: off時isPaginationEnabled為false同時排序sorting.length 0或篩選crudFilters.length 0變化后只有分頁啟用時才會把頁碼重置回第 1 頁if (sorting.length 0 isPaginationEnabled !isFirstRender) { setCurrentPage(1); }與排序、篩選的聯(lián)動換頁重置除了分頁本身源碼中還有兩處值得注意的聯(lián)動邏輯packages/react-table/src/useTable/index.ts 第 135-176 行排序變化sorting變更時將 TanStack 的排序狀態(tài)轉(zhuǎn)換回 Refine 的CrudSortingdesc?desc其余為asc并通過setSorters寫回 core觸發(fā)重新請求列篩選變化columnFilters變更時通過工具函數(shù)columnFiltersToCrudFilters/crudFiltersToColumnFilters與 core 的 filters 狀態(tài)雙向同步并補回被移除的篩選器getRemovedFilters。二者的共同點是在分頁啟用且非首次渲染時把頁碼重置回第 1 頁避免用戶停留在第 5 頁時排序/篩選導(dǎo)致越界。測試用例同樣覆蓋了這些行為index.spec.ts中針對sorters.mode、filters.mode的it.each用例驗證manualSorting/manualFiltering與服務(wù)端模式的對應(yīng)關(guān)系。URL 狀態(tài)同步syncWithLocation如果希望分頁狀態(tài)以及其他表格狀態(tài)同步到 URL 查詢參數(shù)從而支持收藏、分享鏈接可以啟用syncWithLocationuseTable({ refineCoreProps: { syncWithLocation: true, }, });啟用后pageIndex、pageSize等狀態(tài)會自動編碼進(jìn) URL queryURL 變化時表格狀態(tài)也會隨之恢復(fù)。默認(rèn)它會讀取Refine組件上的syncWithLocation全局配置。進(jìn)階客戶端分頁與關(guān)閉分頁按數(shù)據(jù)規(guī)模選擇模式// 客戶端分頁全量拉取、本地切片 useTable({ refineCoreProps: { pagination: { mode: client, }, }, }); // 關(guān)閉分頁一次拉取全部記錄 useTable({ refineCoreProps: { pagination: { mode: off, }, }, });注意客戶端分頁模式下TanStack Table 的getFilteredRowModel、getSortedRowModel等客戶端行模型仍然可用源碼中僅在對應(yīng)服務(wù)端模式開啟時才置為undefined因此客戶端分頁可以方便地與客戶端排序/篩選組合使用。參考示例倉庫內(nèi)完整的可運行示例見 examples/table-react-table-basic它與本指南的分頁示例同源對應(yīng)文檔頁面的 CodeSandbox 鏈接。相關(guān)文檔還包括useTable Hook 完整參考屬性 / 返回值 / FAQTanStack Table 集成介紹Tables 指南guides-conceptscore useTable 數(shù)據(jù) Hook 文檔適配器源碼 與 測試用例【免費下載鏈接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.項目地址: https://gitcode.com/GitHub_Trending/re/refine創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考