Skip to content

Commit 795e8e4

Browse files
committed
chore: react table adapter refactor
1 parent 8a24b14 commit 795e8e4

File tree

7 files changed

+183
-103
lines changed

7 files changed

+183
-103
lines changed

examples/react/row-selection/src/main.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ function App() {
2121
const [rowSelection, setRowSelection] = React.useState({})
2222
const [globalFilter, setGlobalFilter] = React.useState<string | undefined>('')
2323

24-
const columns = React.useMemo<Array<ColumnDef<Person>>>(
24+
const columns = React.useMemo<Array<ColumnDef<any, Person>>>(
2525
() => [
2626
{
2727
id: 'select',
@@ -299,7 +299,7 @@ function Filter({
299299
table,
300300
}: {
301301
column: Column<any, any>
302-
table: Table<any>
302+
table: Table<any, any>
303303
}) {
304304
const firstValue = table
305305
.getPreFilteredRowModel()
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import * as React from 'react'
2+
3+
export type Renderable<TProps> = React.ReactNode | React.ComponentType<TProps>
4+
5+
function isReactComponent<TProps>(
6+
component: unknown,
7+
): component is React.ComponentType<TProps> {
8+
return (
9+
isClassComponent(component) ||
10+
typeof component === 'function' ||
11+
isExoticComponent(component)
12+
)
13+
}
14+
15+
function isClassComponent(component: any) {
16+
return (
17+
typeof component === 'function' &&
18+
(() => {
19+
const proto = Object.getPrototypeOf(component)
20+
return proto.prototype && proto.prototype.isReactComponent
21+
})()
22+
)
23+
}
24+
25+
function isExoticComponent(component: any) {
26+
return (
27+
typeof component === 'object' &&
28+
typeof component.$$typeof === 'symbol' &&
29+
['react.memo', 'react.forward_ref'].includes(component.$$typeof.description)
30+
)
31+
}
32+
33+
/**
34+
* If rendering headers, cells, or footers with custom markup, use flexRender instead of `cell.getValue()` or `cell.renderValue()`.
35+
* @example flexRender(cell.column.columnDef.cell, cell.getContext())
36+
*/
37+
export function flexRender<TProps extends object>(
38+
Comp: Renderable<TProps>,
39+
props: TProps,
40+
): React.ReactNode | JSX.Element {
41+
return !Comp ? null : isReactComponent<TProps>(Comp) ? (
42+
<Comp {...props} />
43+
) : (
44+
Comp
45+
)
46+
}
47+
48+
/**
49+
* Component version of `flexRender`. Use this utility component to render headers, cells, or footers with custom markup.
50+
* @example <FlexRender Component={cell.column.columnDef.cell} props={cell.getContext()} />
51+
*/
52+
export function FlexRender<TProps extends object>({
53+
Component,
54+
props,
55+
}: {
56+
Component: Renderable<TProps>
57+
props: TProps
58+
}) {
59+
return flexRender(Component, props)
60+
}

packages/react-table/src/index.tsx

Lines changed: 3 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -1,97 +1,5 @@
1-
import * as React from 'react'
2-
3-
import { _createTable } from '@tanstack/table-core'
4-
import type {
5-
RowData,
6-
TableFeatures,
7-
TableOptions,
8-
TableOptionsResolved,
9-
} from '@tanstack/table-core'
10-
111
export * from '@tanstack/table-core'
122

13-
export type Renderable<TProps> = React.ReactNode | React.ComponentType<TProps>
14-
15-
//
16-
17-
/**
18-
* If rendering headers, cells, or footers with custom markup, use flexRender instead of `cell.getValue()` or `cell.renderValue()`.
19-
*/
20-
export function flexRender<TProps extends object>(
21-
Comp: Renderable<TProps>,
22-
props: TProps,
23-
): React.ReactNode | JSX.Element {
24-
return !Comp ? null : isReactComponent<TProps>(Comp) ? (
25-
<Comp {...props} />
26-
) : (
27-
Comp
28-
)
29-
}
30-
31-
function isReactComponent<TProps>(
32-
component: unknown,
33-
): component is React.ComponentType<TProps> {
34-
return (
35-
isClassComponent(component) ||
36-
typeof component === 'function' ||
37-
isExoticComponent(component)
38-
)
39-
}
40-
41-
function isClassComponent(component: any) {
42-
return (
43-
typeof component === 'function' &&
44-
(() => {
45-
const proto = Object.getPrototypeOf(component)
46-
return proto.prototype && proto.prototype.isReactComponent
47-
})()
48-
)
49-
}
50-
51-
function isExoticComponent(component: any) {
52-
return (
53-
typeof component === 'object' &&
54-
typeof component.$$typeof === 'symbol' &&
55-
['react.memo', 'react.forward_ref'].includes(component.$$typeof.description)
56-
)
57-
}
58-
59-
export function useTable<
60-
TFeatures extends TableFeatures,
61-
TData extends RowData,
62-
>(options: TableOptions<TFeatures, TData>) {
63-
// Compose in the generic options to the user options
64-
const resolvedOptions: TableOptionsResolved<TFeatures, TData> = {
65-
state: {}, // Dummy state
66-
onStateChange: () => {}, // noop
67-
renderFallbackValue: null,
68-
...options,
69-
}
70-
71-
// Create a new table and store it in state
72-
const [tableRef] = React.useState(() => ({
73-
current: _createTable<TFeatures, TData>(resolvedOptions),
74-
}))
75-
76-
// By default, manage table state here using the table's initial state
77-
const [state, setState] = React.useState(() => tableRef.current.initialState)
78-
79-
// Compose the default state above with any user state. This will allow the user
80-
// to only control a subset of the state if desired.
81-
tableRef.current.setOptions((prev) => ({
82-
...prev,
83-
...options,
84-
state: {
85-
...state,
86-
...options.state,
87-
},
88-
// Similarly, we'll maintain both our internal state and any user-provided
89-
// state.
90-
onStateChange: (updater) => {
91-
setState(updater)
92-
options.onStateChange?.(updater)
93-
},
94-
}))
95-
96-
return tableRef.current
97-
}
3+
export * from './useTable'
4+
export * from './FlexRender'
5+
export * from './tableFactory'
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { _createTable, createColumnHelper } from '@tanstack/table-core'
2+
import { useTable } from './useTable'
3+
import type {
4+
ColumnHelper,
5+
RequiredKeys,
6+
RowData,
7+
TableFeatures,
8+
TableOptions,
9+
} from '@tanstack/table-core'
10+
11+
export function tableFactory<
12+
TFeatures extends TableFeatures,
13+
TData extends RowData,
14+
TOptions extends RequiredKeys<
15+
Omit<TableOptions<TFeatures, TData>, 'columns' | 'data'>,
16+
'_features'
17+
>,
18+
>(
19+
options: TOptions & { TData: TData },
20+
): {
21+
columnHelper: ColumnHelper<TFeatures, TData>
22+
options: TOptions
23+
useTable: typeof useTable
24+
} {
25+
return {
26+
columnHelper: createColumnHelper(),
27+
options,
28+
useTable,
29+
}
30+
}
31+
32+
//test
33+
34+
// type Person = {
35+
// firstName: string
36+
// lastName: string
37+
// age: number
38+
// }
39+
40+
// const factory = tableFactory({
41+
// TData: {} as Person,
42+
// _features: { RowSelection: {} },
43+
// })
44+
45+
// const columns = [
46+
// factory.columnHelper.accessor('firstName', { header: 'First Name' }),
47+
// factory.columnHelper.accessor('lastName', { header: 'Last Name' }),
48+
// factory.columnHelper.accessor('age', { header: 'Age' }),
49+
// factory.columnHelper.display({ header: 'Actions', id: 'actions' }),
50+
// ]
51+
52+
// const data: Array<Person> = []
53+
54+
// const table = factory.useTable({
55+
// columns,
56+
// data,
57+
// })
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import * as React from 'react'
2+
3+
import {
4+
_createTable,
5+
builtInFeatures,
6+
getInitialTableState,
7+
} from '@tanstack/table-core'
8+
import type {
9+
RowData,
10+
Table,
11+
TableFeatures,
12+
TableOptions,
13+
TableOptionsResolved,
14+
TableState,
15+
} from '@tanstack/table-core'
16+
17+
function useTableRef<TFeatures extends TableFeatures, TData extends RowData>(
18+
options: TableOptionsResolved<TFeatures, TData>,
19+
): Table<TFeatures, TData> {
20+
const tableRef = React.useRef<Table<TFeatures, TData>>()
21+
22+
if (!tableRef.current) {
23+
tableRef.current = _createTable(options)
24+
}
25+
26+
return tableRef.current
27+
}
28+
29+
/**
30+
* Will re-render the table whenever the state or options change. Works just like the `useReactTable` from v8.
31+
* @example const table = useTable({ columns, data, state, ...options })
32+
*/
33+
export function useTable<
34+
TFeatures extends TableFeatures,
35+
TData extends RowData,
36+
>(tableOptions: TableOptions<TFeatures, TData>): Table<TFeatures, TData> {
37+
const [state, setState] = React.useState<TableState>(
38+
getInitialTableState(builtInFeatures, tableOptions.initialState),
39+
)
40+
41+
const statefulOptions: TableOptionsResolved<TFeatures, TData> = {
42+
...tableOptions,
43+
state: { ...state, ...tableOptions.state },
44+
onStateChange: (updater) => {
45+
setState(updater)
46+
tableOptions.onStateChange?.(updater)
47+
},
48+
}
49+
50+
const table = useTableRef(statefulOptions)
51+
52+
table.setOptions((prev) => ({ ...prev, ...statefulOptions })) //force re-render when state or options change
53+
54+
return table
55+
}

packages/table-core/src/core/table/createTable.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ import type {
2828
TableState,
2929
} from '../../types'
3030

31-
const coreFeatures = { Tables, Rows, Headers, Columns, Cells }
31+
export const coreFeatures = { Tables, Rows, Headers, Columns, Cells }
3232

33-
const builtInFeatures = {
33+
export const builtInFeatures = {
3434
ColumnFaceting,
3535
ColumnFiltering,
3636
ColumnGrouping,
@@ -49,10 +49,10 @@ const builtInFeatures = {
4949
}
5050

5151
export function getInitialTableState(
52-
features: Array<TableFeature>,
52+
features: TableFeatures,
5353
initialState: Partial<TableState> | undefined = {},
5454
): TableState {
55-
features.forEach((feature) => {
55+
Object.values(features).forEach((feature) => {
5656
initialState = feature._getInitialState?.(initialState) ?? initialState
5757
})
5858
return initialState as TableState
@@ -83,7 +83,7 @@ export function _createTable<
8383
return Object.assign(obj, feature._getDefaultOptions?.(table))
8484
}, {}) as TableOptionsResolved<TFeatures, TData>
8585

86-
const initialState = getInitialTableState(featuresList, options.initialState)
86+
const initialState = getInitialTableState(_features, options.initialState)
8787

8888
const coreInstance: Table_CoreProperties<TFeatures, TData> = {
8989
_features,

packages/table-core/src/helpers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import type {
99
TableFeatures,
1010
TableOptions,
1111
} from './types'
12-
import type { DeepKeys, DeepValue } from './utils.types'
12+
import type { DeepKeys, DeepValue, RequiredKeys } from './utils.types'
1313

1414
export type ColumnHelper<
1515
TFeatures extends TableFeatures,

0 commit comments

Comments
 (0)