框架
版本
企业版

展开指南

示例

想直接看实现?可以参考以下示例

API

展开 API

展开特性指南

展开是一项功能,允许您显示和隐藏与特定行相关联的额外数据行。当您拥有分层数据并希望允许用户从更高级别深入了解数据时,这会很有用。或者,它还可以用于显示与行相关的附加信息。

展开特性的不同用例

TanStack Table 中展开特性有多种用例,将在下文讨论。

  1. 展开子行(子行、聚合行等)
  2. 展开自定义 UI(详细信息面板、子表等)

启用客户端展开

要使用客户端展开特性,您需要在表选项中定义 `getExpandedRowModel` 函数。此函数负责返回展开的行模型。

ts
const table = useReactTable({
  // other options...
  getExpandedRowModel: getExpandedRowModel(),
})
const table = useReactTable({
  // other options...
  getExpandedRowModel: getExpandedRowModel(),
})

展开数据可以包含表行或您希望显示的任何其他数据。在本指南中,我们将讨论如何处理这两种情况。

行作为展开数据

展开的行本质上是子行,它们继承与其父行相同的列结构。如果您的数据对象已经包含这些展开的行数据,您可以使用 getSubRows 函数来指定这些子行。但是,如果您的数据对象不包含展开的行数据,它们可以被视为自定义展开数据,这将在下一节中讨论。

例如,如果您有这样的数据对象

ts
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` 数组作为展开行。表实例现在将知道在每行中查找子行。

ts
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

在某些情况下,您可能希望显示额外的详细信息或信息,这些信息可能包含也可能不包含在您的表数据对象中,例如行的展开数据。这种展开行的 UI 在多年来有很多名称,包括“可展开行”、“详细信息面板”、“子组件”等。

默认情况下,row.getCanExpand() 行实例 API 将返回 `false`,除非它在行上找到 subRows。可以通过在表实例选项中实现自己的 getRowCanExpand 函数来覆盖此行为。

ts
//...
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>
//...

展开行状态

如果您需要控制表中行的展开状态,您可以使用 `expanded` state 和 `onExpandedChange` 选项来实现。这允许您根据您的要求管理展开状态。

ts
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 类型定义如下

ts
type ExpandedState = true | Record<string, boolean>
type ExpandedState = true | Record<string, boolean>

如果 `ExpandedState` 为 `true`,则表示所有行都已展开。如果它是一个记录(record),则只有 ID 作为键存在于记录中且其值为 `true` 的行才会被展开。例如,如果展开状态为 `{ row1: true, row2: false }`,则表示 ID 为 `row1` 的行已展开,而 ID 为 `row2` 的行未展开。表使用此状态来确定哪些行已展开并且应该显示它们的子行(如果有)。

展开行的 UI 切换处理程序

TanStack Table 不会自动为您的表添加展开数据的切换处理程序 UI。您应该在每一行的 UI 中手动添加它,以允许用户展开和折叠行。例如,您可以在列定义中添加一个按钮 UI。

ts
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 选项来控制过滤过程深入子层级的深度。此选项允许您指定过滤器应考虑的子行的最大深度。

ts
//...
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 选项。

ts
const table = useReactTable({
  // other options...
  paginateExpandedRows: false,
})
const table = useReactTable({
  // other options...
  paginateExpandedRows: false,
})

固定展开的行

固定展开的行与固定普通行的工作方式相同。您可以将展开的行固定到表的顶部或底部。有关行固定的更多信息,请参阅 固定指南

排序展开的行

默认情况下,展开的行会与表的其余部分一起排序。

手动展开(服务器端)

如果您正在进行服务器端展开,可以通过将 `manualExpanding` 选项设置为 `true` 来启用手动行展开。这意味着将不使用 getExpandedRowModel 来展开行,并且您需要自己处理数据模型中的展开。

ts
const table = useReactTable({
  // other options...
  manualExpanding: true,
})
const table = useReactTable({
  // other options...
  manualExpanding: true,
})
我们的合作伙伴
Code Rabbit
AG Grid
订阅 Bytes

您的每周 JavaScript 资讯。每周一免费发送给超过 10 万开发者。

Bytes

无垃圾邮件。您可以随时取消订阅。

订阅 Bytes

您的每周 JavaScript 资讯。每周一免费发送给超过 10 万开发者。

Bytes

无垃圾邮件。您可以随时取消订阅。