想跳过直接看实现吗?查看这些示例
展开是一个功能,允许您显示和隐藏与特定行相关的额外数据。当您有层级数据,并且希望允许用户从更高级别向下钻取数据时,这会很有用。或者,它可用于显示与行相关的其他信息。
TanStack Table 中的展开功能有多种用例,将在下面讨论。
要使用客户端展开功能,您需要在表格选项中定义 getExpandedRowModel 函数。此函数负责返回展开的行模型。
const table = useReactTable({
// other options...
getExpandedRowModel: getExpandedRowModel(),
})
const table = useReactTable({
// other options...
getExpandedRowModel: getExpandedRowModel(),
})
展开数据可以包含表格行或您想要显示的任何其他数据。我们将在本指南中讨论如何处理这两种情况。
展开行本质上是子行,它们继承与其父行相同的列结构。如果您的数据对象已经包含这些展开行数据,您可以利用 getSubRows 函数来指定这些子行。但是,如果您的数据对象不包含展开行数据,则可以将它们视为自定义展开数据,这将在下一节中讨论。
例如,如果您有这样的数据对象
type Person = {
id: number
name: string
age: number
children?: Person[] | undefined
}
const data: Person[] = [
{ id: 1,
name: 'John',
age: 30,
children: [
{ id: 2, name: 'Jane', age: 5 },
{ id: 5, name: 'Jim', age: 10 }
]
},
{ id: 3,
name: 'Doe',
age: 40,
children: [
{ id: 4, name: 'Alice', age: 10 }
]
},
]
type Person = {
id: number
name: string
age: number
children?: Person[] | undefined
}
const data: Person[] = [
{ id: 1,
name: 'John',
age: 30,
children: [
{ id: 2, name: 'Jane', age: 5 },
{ id: 5, name: 'Jim', age: 10 }
]
},
{ id: 3,
name: 'Doe',
age: 40,
children: [
{ id: 4, name: 'Alice', age: 10 }
]
},
]
那么您可以使用 getSubRows 函数返回每行中的 children 数组作为展开行。表格实例现在将了解在每行上查找子行的位置。
const table = useReactTable({
// other options...
getSubRows: (row) => row.children, // return the children array as sub-rows
getCoreRowModel: getCoreRowModel(),
getExpandedRowModel: getExpandedRowModel(),
})
const table = useReactTable({
// other options...
getSubRows: (row) => row.children, // return the children array as sub-rows
getCoreRowModel: getCoreRowModel(),
getExpandedRowModel: getExpandedRowModel(),
})
注意: 您可以有一个复杂的 getSubRows 函数,但请记住它将为每一行和每个子行运行。如果该函数未优化,这可能会很耗费性能。不支持异步函数。
在某些情况下,您可能希望显示额外的详细信息或信息,这些信息可能不属于您的表格数据对象,例如行的展开数据。多年来,这种展开行 UI 有许多名称,包括“可展开行”、“详细信息面板”、“子组件”等。
默认情况下,除非在行上找到 subRows,否则 row.getCanExpand() 行实例 API 将返回 false。这可以通过在表格实例选项中实现您自己的 getRowCanExpand 函数来覆盖。
//...
const table = useReactTable({
// other options...
getRowCanExpand: (row) => true, // Add your logic to determine if a row can be expanded. True means all rows include expanded data
getCoreRowModel: getCoreRowModel(),
getExpandedRowModel: getExpandedRowModel(),
})
//...
<tbody>
{table.getRowModel().rows.map((row) => (
<React.Fragment key={row.id}>
{/* Normal row UI */}
<tr>
{row.getVisibleCells().map((cell) => (
<td key={cell.id}>
<FlexRender
render={cell.column.columnDef.cell}
props={cell.getContext()}
/>
</td>
))}
</tr>
{/* If the row is expanded, render the expanded UI as a separate row with a single cell that spans the width of the table */}
{row.getIsExpanded() && (
<tr>
<td colSpan={row.getAllCells().length}> // The number of columns you wish to span for the expanded data if it is not a row that shares the same columns as the parent row
// Your custom UI goes here
</td>
</tr>
)}
</React.Fragment>
))}
</tbody>
//...
//...
const table = useReactTable({
// other options...
getRowCanExpand: (row) => true, // Add your logic to determine if a row can be expanded. True means all rows include expanded data
getCoreRowModel: getCoreRowModel(),
getExpandedRowModel: getExpandedRowModel(),
})
//...
<tbody>
{table.getRowModel().rows.map((row) => (
<React.Fragment key={row.id}>
{/* Normal row UI */}
<tr>
{row.getVisibleCells().map((cell) => (
<td key={cell.id}>
<FlexRender
render={cell.column.columnDef.cell}
props={cell.getContext()}
/>
</td>
))}
</tr>
{/* If the row is expanded, render the expanded UI as a separate row with a single cell that spans the width of the table */}
{row.getIsExpanded() && (
<tr>
<td colSpan={row.getAllCells().length}> // The number of columns you wish to span for the expanded data if it is not a row that shares the same columns as the parent row
// Your custom UI goes here
</td>
</tr>
)}
</React.Fragment>
))}
</tbody>
//...
如果您需要控制表格中行的展开状态,您可以使用展开状态和 onExpandedChange 选项来做到这一点。这允许您根据您的要求管理展开状态。
const [expanded, setExpanded] = useState<ExpandedState>({})
const table = useReactTable({
// other options...
state: {
expanded: expanded, // must pass expanded state back to the table
},
onExpandedChange: setExpanded
})
const [expanded, setExpanded] = useState<ExpandedState>({})
const table = useReactTable({
// other options...
state: {
expanded: expanded, // must pass expanded state back to the table
},
onExpandedChange: setExpanded
})
ExpandedState 类型定义如下
type ExpandedState = true | Record<string, boolean>
type ExpandedState = true | Record<string, boolean>
如果 ExpandedState 为 true,则表示所有行都已展开。如果它是一个记录,则只有 ID 作为记录中的键存在且其值设置为 true 的行才会被展开。例如,如果展开状态为 { row1: true, row2: false },则表示 ID 为 row1 的行已展开,而 ID 为 row2 的行未展开。表格使用此状态来确定哪些行已展开,并应显示其 subRows(如果有)。
TanStack 表格不会为您的表格中的展开数据添加切换处理程序 UI。您应该在每行的 UI 中手动添加它,以允许用户展开和折叠行。例如,您可以在列定义中添加一个按钮 UI。
const columns = [
{
accessorKey: 'name',
header: 'Name',
},
{
accessorKey: 'age',
header: 'Age',
},
{
header: 'Children',
cell: ({ row }) => {
return row.getCanExpand() ?
<button
onClick={row.getToggleExpandedHandler()}
style={{ cursor: 'pointer' }}
>
{row.getIsExpanded() ? '👇' : '👉'}
</button>
: '';
},
},
]
const columns = [
{
accessorKey: 'name',
header: 'Name',
},
{
accessorKey: 'age',
header: 'Age',
},
{
header: 'Children',
cell: ({ row }) => {
return row.getCanExpand() ?
<button
onClick={row.getToggleExpandedHandler()}
style={{ cursor: 'pointer' }}
>
{row.getIsExpanded() ? '👇' : '👉'}
</button>
: '';
},
},
]
默认情况下,筛选过程从父行开始并向下移动。这意味着如果父行被筛选器排除,则其所有子行也将被排除。但是,您可以使用 filterFromLeafRows 选项来更改此行为。启用此选项后,筛选过程从叶(子)行开始并向上移动。这确保了只要父行的至少一个子行或孙子行满足筛选条件,父行就会包含在筛选结果中。此外,您可以使用 maxLeafRowFilterDepth 选项来控制筛选过程深入子层级的深度。此选项允许您指定筛选器应考虑的子行的最大深度。
//...
const table = useReactTable({
// other options...
getSubRows: row => row.subRows,
getCoreRowModel: getCoreRowModel(),
getFilteredRowModel: getFilteredRowModel(),
getExpandedRowModel: getExpandedRowModel(),
filterFromLeafRows: true, // search through the expanded rows
maxLeafRowFilterDepth: 1, // limit the depth of the expanded rows that are searched
})
//...
const table = useReactTable({
// other options...
getSubRows: row => row.subRows,
getCoreRowModel: getCoreRowModel(),
getFilteredRowModel: getFilteredRowModel(),
getExpandedRowModel: getExpandedRowModel(),
filterFromLeafRows: true, // search through the expanded rows
maxLeafRowFilterDepth: 1, // limit the depth of the expanded rows that are searched
})
默认情况下,展开行与表格的其余部分一起分页(这意味着展开行可能跨越多个页面)。如果您想禁用此行为(这意味着展开行将始终在其父页面上呈现。这也意味着将呈现比设置的页面大小更多的行),您可以使用 paginateExpandedRows 选项。
const table = useReactTable({
// other options...
paginateExpandedRows: false,
})
const table = useReactTable({
// other options...
paginateExpandedRows: false,
})
固定展开行的工作方式与固定常规行相同。您可以将展开行固定到表格的顶部或底部。有关行固定的更多信息,请参阅固定指南。
默认情况下,展开行与表格的其余部分一起排序。
如果您正在进行服务器端展开,则可以通过将 manualExpanding 选项设置为 true 来启用手动行展开。这意味着 getExpandedRowModel 将不会用于展开行,您需要您自己的数据模型中执行展开。
const table = useReactTable({
// other options...
manualExpanding: true,
})
const table = useReactTable({
// other options...
manualExpanding: true,
})
您的每周 JavaScript 新闻。每周一免费发送给超过 10 万名开发者。