Lzh on GitHub

表格状态 (Vue) 指南

TanStack Table 有一个简单的底层内部状态管理系统来存储和管理表格的状态。它还允许你选择性地提取你需要自己管理的状态。本指南将引导你了解与表格状态交互和管理的不同方式。

访问表格状态

你不需要进行任何特殊设置就可以让表格状态工作。如果你在 stateinitialState 或任何 on[State]Change 表格选项中什么都不传递,表格将 在内部管理自己的状态。你可以使用 table.getState() 表格实例 API 访问此内部状态的任何部分。

const table = useVueTable({
  columns,
  data: dataRef, // 响应式数据支持
  // ...
})

console.log(table.getState()) // 访问整个内部状态
console.log(table.getState().rowSelection) // 只访问行选择状态

使用响应式数据

v8.20.0 新增

useVueTable 钩子现在支持 响应式数据。这意味着你可以将包含数据的 Vue refcomputed 传递给 data 选项。表格将自动对数据的更改做出反应。

const columns = [
  { accessor: 'id', Header: 'ID' },
  { accessor: 'name', Header: 'Name' }
]

const dataRef = ref([
  { id: 1, name: 'John' },
  { id: 2, name: 'Jane' }
])

const table = useVueTable({
  columns,
  data: dataRef, // 传递响应式数据 ref
})

// 稍后,更新 dataRef 将自动更新表格
dataRef.value = [
  { id: 1, name: 'John' },
  { id: 2, name: 'Jane' },
  { id: 3, name: 'Doe' }
]

⚠️ 为了性能原因,底层使用了 shallowRef,这意味着数据不是深度响应式的,只有 .value 是。要更新数据,你必须直接修改数据

const dataRef = ref([
  { id: 1, name: 'John' },
  { id: 2, name: 'Jane' }
])

// 这不会更新表格 ❌
dataRef.value.push({ id: 4, name: 'John' })

// 这会更新表格 ✅
dataRef.value = [
  ...dataRef.value,
  { id: 4, name: 'John' }
]

自定义初始状态

如果你只需要为某些状态 自定义其初始默认值,你仍然不需要自己管理任何状态。你只需在表格实例的 initialState 选项中设置值即可。

const table = useVueTable({
  columns,
  data,
  initialState: {
    columnOrder: ['age', 'firstName', 'lastName'], // 自定义初始列顺序
    columnVisibility: {
      id: false // 默认隐藏 id 列
    },
    expanded: true, // 默认展开所有行
    sorting: [
      {
        id: 'age',
        desc: true // 默认按年龄降序排序
      }
    ]
  },
  // ...
})

注意: 对于每个特定状态,只能在 initialStatestate 中指定,不能两者都指定。如果你将特定状态值同时传递给 initialStatestatestate 中初始化的状态将覆盖 initialState 中的任何对应值。

受控状态 (Controlled State)

如果你需要在应用程序的其他区域轻松访问表格状态,TanStack Table 可以让你轻松地在自己的状态管理系统中 控制和管理任何或所有表格状态。你可以通过将你自己的状态和状态管理函数传递给 stateon[State]Change 表格选项来实现这一点。

单独受控状态

你可以只控制你需要轻松访问的状态。如果你不需要,你 不必控制所有表格状态。建议仅根据具体情况控制你需要的状态。

为了控制特定状态,你需要同时将相应的 state 值和 on[State]Change 函数传递给表格实例。

让我们以 “手动” 服务器端数据获取场景中的过滤、排序和分页为例。你可以将过滤、排序和分页状态存储在你自己的状态管理中,但如果你的 API 不关心这些值,则可以省略其他状态,如列顺序、列可见性等。

const columnFilters = ref([]) // 无默认过滤器
const sorting = ref([{
  id: 'age',
  desc: true, // 默认按年龄降序排序
}])
const pagination = ref({ pageIndex: 0, pageSize: 15 })

// 使用我们的受控状态值来获取数据
const tableQuery = useQuery({
  queryKey: ['users', columnFilters, sorting, pagination],
  queryFn: () => fetchUsers(columnFilters, sorting, pagination),
  // ...
})

const table = useVueTable({
  columns,
  data: tableQuery.data,
  // ...
  state: {
    get columnFilters() {
      return columnFilters.value
    },
    get sorting() {
      return sorting.value
    },
    get pagination() {
      return pagination.value
    }
  },
  onColumnFiltersChange: updater => {
    columnFilters.value =
      updater instanceof Function
        ? updater(columnFilters.value)
        : updater
  },
  onSortingChange: updater => {
    sorting.value =
      updater instanceof Function
        ? updater(sorting.value)
        : updater
  },
  onPaginationChange: updater => {
    pagination.value =
      updater instanceof Function
        ? updater(pagination.value)
        : updater
  },
})
// ...

完全受控状态

或者,你可以使用 onStateChange 表格选项控制整个表格状态。它将把整个表格状态提升到你自己的状态管理系统。请注意这种方法,因为你可能会发现将一些频繁变化的状态值(如 columnSizingInfo 状态)提升到 React 树的顶层可能会导致不良的性能问题。

要使此功能正常工作,可能还需要一些额外的技巧。如果你使用 onStateChange 表格选项,则 state 的初始值必须填充所有相关状态值,以便你想要使用的所有功能都能正常工作。你可以手动输入所有初始状态值,或者以特殊方式使用 table.setOptions API,如下所示。

// 创建一个具有默认状态值的表格实例
const table = useVueTable({
  get columns() {
    return columns.value
  },
  data,
  // ... 注意:`state` 值尚未传入
})

const state = ref({
  ...table.initialState,
  pagination: {
    pageIndex: 0,
    pageSize: 15
  }
})

const setState = updater => {
  state.value = updater instanceof Function ? updater(state.value) : updater
}

// 使用 table.setOptions API 将我们完全受控的状态合并到表格实例上
table.setOptions(prev => ({
  ...prev, // 保留我们上面设置的任何其他选项
  get state() {
    return state.value
  },
  onStateChange: setState // 任何状态更改都将推送到我们自己的状态管理
}))

状态更改回调 (On State Change Callbacks)

到目前为止,我们已经看到了 on[State]ChangeonStateChange 表格选项如何将表格状态更改“提升”到我们自己的状态管理中。然而,关于使用这些选项,有几点你应该注意。

  1. 状态更改回调必须在 state 选项中具有其相应的状态值。
    指定 on[State]Change 回调会告诉表格实例这将是一个受控状态。如果你不指定相应的 state 值,该状态将以其初始值**“冻结”**。
    const sorting = ref([])
    const setSorting = updater => {
      sorting.value = updater instanceof Function ? updater(sorting.value) : updater
    }
    
    // ...
    
    const table = useVueTable({
      columns,
      data,
      // ...
      state: {
        get sorting() {
          return sorting // 必需,因为我们正在使用 `onSortingChange`
        },
      },
      onSortingChange: setSorting, // 使 `state.sorting` 受控
    })
    
  2. 更新器可以是原始值或回调函数。
    on[State]ChangeonStateChange 回调的工作方式与 React 中的 setState 函数完全相同。更新器值可以是一个新状态值,也可以是一个接受前一个状态值并返回新状态值的回调函数。
    这意味着什么?这意味着如果你想在任何 on[State]Change 回调中添加一些额外逻辑,你可以这样做,但你需要检查传入的更新器值是函数还是值。
    这就是为什么我们在上面的 setState 函数中有 updater instanceof Function 检查。此检查允许我们在同一个函数中处理原始值和回调函数。

状态类型 (State Types)

TanStack Table 中的所有复杂状态都有自己的 TypeScript 类型,你可以导入并使用它们。这对于确保你为正在控制的状态值使用正确的数据结构和属性非常方便。

import { useVueTable, type SortingState } from '@tanstack/vue-table'

// ...

const sorting = ref<SortingState[]>([
  {
    id: 'age', // 你应该获得 `id` 和 `desc` 属性的自动补全
    desc: true,
  }
])

希望这份关于 TanStack Table (Vue) 状态管理的详细指南对你有所帮助!你是否正在考虑在你的项目中采用 TanStack Table?