-
-
Notifications
You must be signed in to change notification settings - Fork 3.4k
feat: add existingTable param to useReactTable #6123
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
this enables framework agnostic table adapters to create a table outside of the react context, but then use the existing react package to interact with it as a regular table
🦋 Changeset detectedLatest commit: e468632 The changes in this PR will be included in the next version bump. This PR includes changesets to release 2 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
WalkthroughAdds an optional Changes
Sequence Diagram(s)sequenceDiagram
participant Caller
participant AdapterHook as Adapter Hook
participant createTable as createTable
note right of AdapterHook `#D6EAF8`: receives options, optional existingTable
Caller->>AdapterHook: call(options, existingTable?)
alt existingTable provided
AdapterHook-->>Caller: return existingTable (wired/reactive)
else no existingTable
AdapterHook->>createTable: createTable(resolvedOptions)
createTable-->>AdapterHook: new Table instance
AdapterHook-->>Caller: return new Table (wired/reactive)
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20–30 minutes
Poem
Pre-merge checks and finishing touches✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/react-table/src/index.tsx (1)
76-93: State initialization mismatch when reusing external tables.When an
existingTableis provided that has already been used (i.e., its current state differs from itsinitialState), line 76 initializes the React state usingtableRef.current.initialStaterather than the current state, potentially discarding prior modifications. This React state is then synced back to the table viasetOptions(lines 80-93), causing unexpected state resets.Example scenario:
- External table created with
initialState: { pagination: { pageIndex: 0 } }- Table used elsewhere, state updated to
{ pagination: { pageIndex: 5 } }- Table passed to
useReactTableasexistingTable- Hook initializes React state to
initialState(pageIndex: 0) on line 76- Hook syncs this stale state back to the table via
setOptions- Result: Page index unexpectedly resets from 5 to 0
Suggested fix:
Use the table's current state instead of its initial state:- const [state, setState] = React.useState(() => tableRef.current.initialState) + const [state, setState] = React.useState(() => tableRef.current.getState())Verify this doesn't break state management for newly created tables and document the intended usage pattern for
existingTable.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
.changeset/beige-singers-admire.md(1 hunks)packages/react-table/src/index.tsx(3 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-09-15T15:05:14.117Z
Learnt from: stevenwjy
Repo: TanStack/table PR: 6094
File: packages/table-core/src/utils/getGroupedRowModel.ts:74-78
Timestamp: 2025-09-15T15:05:14.117Z
Learning: In TanStack Table's Row interface, the `subRows` property is typed as `Row<TData>[]` (not optional) and the `createRow` function always initializes it as an empty array, so `subRows` is guaranteed to never be undefined.
Applied to files:
packages/react-table/src/index.tsx
📚 Learning: 2025-09-15T15:05:14.117Z
Learnt from: stevenwjy
Repo: TanStack/table PR: 6094
File: packages/table-core/src/utils/getGroupedRowModel.ts:74-78
Timestamp: 2025-09-15T15:05:14.117Z
Learning: In TanStack Table's Row interface, the `subRows` property is typed as `Row<TData>[]` (not optional) and the `createRow` function always initializes it as an empty array using `subRows ?? []`, so `subRows` is guaranteed to never be undefined.
Applied to files:
packages/react-table/src/index.tsx
🧬 Code graph analysis (1)
packages/react-table/src/index.tsx (2)
packages/table-core/src/types.ts (2)
TableOptions(173-177)Table(137-152)packages/table-core/src/core/table.ts (1)
createTable(283-527)
🔇 Additional comments (3)
packages/react-table/src/index.tsx (2)
9-9: LGTM: Table type import is necessary and correct.The
Tabletype import is required to type the newexistingTableparameter and follows the existing import pattern from@tanstack/table-core.
70-73: LGTM: Table initialization logic is correct.The conditional initialization using
existingTable ?? createTable<TData>(resolvedOptions)correctly uses the provided table when available, falling back to creating a new one. TheuseStatecallback ensures this only executes once per component mount.Note:
resolvedOptions(lines 63-68) is computed regardless of whetherexistingTableis provided, but this is a minor inefficiency and doesn't affect correctness..changeset/beige-singers-admire.md (1)
1-5: LGTM: Changeset is correct.The changeset correctly documents a patch version bump with an appropriate feature description. The format follows standard conventions.
| export function useReactTable<TData extends RowData>( | ||
| options: TableOptions<TData> | ||
| options: TableOptions<TData>, | ||
| existingTable?: Table<TData> | ||
| ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Add JSDoc documentation for the new parameter.
The existingTable parameter introduces a significant new usage pattern but lacks documentation explaining when and how it should be used, what happens to the existing table's state, and any constraints or gotchas.
Add JSDoc to document the parameter:
+/**
+ * Hook to create or reuse a TanStack table instance within React.
+ * @param options - Table configuration options
+ * @param existingTable - Optional pre-created table instance. When provided, this table will be used instead of creating a new one. Note: The hook will manage state internally and sync it with the table via setOptions on each render.
+ */
export function useReactTable<TData extends RowData>(
options: TableOptions<TData>,
existingTable?: Table<TData>
) {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export function useReactTable<TData extends RowData>( | |
| options: TableOptions<TData> | |
| options: TableOptions<TData>, | |
| existingTable?: Table<TData> | |
| ) { | |
| /** | |
| * Hook to create or reuse a TanStack table instance within React. | |
| * @param options - Table configuration options | |
| * @param existingTable - Optional pre-created table instance. When provided, this table will be used instead of creating a new one. Note: The hook will manage state internally and sync it with the table via setOptions on each render. | |
| */ | |
| export function useReactTable<TData extends RowData>( | |
| options: TableOptions<TData>, | |
| existingTable?: Table<TData> | |
| ) { |
🤖 Prompt for AI Agents
In packages/react-table/src/index.tsx around lines 58 to 61, the new
existingTable parameter of useReactTable is undocumented; add a JSDoc block
above the function that documents the parameter with: a clear description of
when to pass an existing Table instance, what parts of the existing table’s
state (e.g., selectedRows, columnOrder, sorting, pagination) are adopted or left
untouched, whether the hook will mutate or replace the provided table (and if it
clones it), lifecycle/ownership expectations (caller retains responsibility for
cleanup), accepted types/nullability (Table<TData> | undefined), and any gotchas
or constraints (concurrent use, prop mismatch, incompatible options). Keep it
concise and include @param existingTable and a short @returns note if not
already present.
this enables framework agnostic table adapters to create a table outside of the framework context, but then use the existing packages to interact with it as a regular table
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (5)
packages/svelte-table/src/index.ts (1)
71-74: Consider importingTabletype at the top for consistency.The inline type import
import('@tanstack/table-core').Table<TData>works correctly but is inconsistent with the other imports from@tanstack/table-coreat the top of the file. Consider addingTableto the imports on lines 1-6 for better consistency and readability.Additionally, consider adding JSDoc to document the purpose and usage of the
existingTableparameter, particularly noting that it should be a freshly created table instance that the adapter will manage.Apply this diff to improve import consistency:
import { RowData, createTable, TableOptions, TableOptionsResolved, + Table, } from '@tanstack/table-core'And update the function signature:
export function createSvelteTable<TData extends RowData>( options: ReadableOrVal<TableOptions<TData>>, - existingTable?: import('@tanstack/table-core').Table<TData> + existingTable?: Table<TData> ) {packages/lit-table/src/index.ts (1)
39-48: Consider documenting the one-time initialization behavior.The
existingTableparameter is only used during the first call totable()for a given controller instance. Subsequent calls reuse the cachedthis.tableInstance. While this behavior seems correct, adding a JSDoc comment would help clarify this for API consumers.Example documentation:
+ /** + * Get or initialize the table instance. + * @param options - Table configuration options + * @param existingTable - Optional pre-existing table instance (only used on first call) + * @returns The table instance + */ public table(options: TableOptions<TData>, existingTable?: Table<TData>) {packages/solid-table/src/index.tsx (1)
27-30: Implementation looks good; consider importingTableat the top for consistency.The function signature change correctly adds an optional
existingTableparameter, maintaining backward compatibility. The inline import syntaximport('@tanstack/table-core').Table<TData>works correctly but differs from the conventional pattern used for other imports in this file.If desired, you can import
Tableat the top for consistency:import { TableOptions, createTable, TableOptionsResolved, RowData, + Table, } from '@tanstack/table-core'Then update the parameter type:
export function createSolidTable<TData extends RowData>( options: TableOptions<TData>, - existingTable?: import('@tanstack/table-core').Table<TData> + existingTable?: Table<TData> ) {packages/vue-table/src/index.ts (2)
54-55: ImportTabletype at the top for consistency.The
existingTableparameter uses an inline import type (import('@tanstack/table-core').Table<TData>), while other types from the same package are imported at the top of the file. For consistency and readability, addTableto the top-level imports.Apply this diff to add the import:
import { TableOptions, createTable, RowData, TableOptionsResolved, + Table, } from '@tanstack/table-core'Then update the function signature:
export function useVueTable<TData extends RowData>( initialOptions: TableOptionsWithReactiveData<TData>, - existingTable?: import('@tanstack/table-core').Table<TData> + existingTable?: Table<TData> ) {
53-56: Consider adding JSDoc to document the new parameter.The new
existingTableparameter lacks documentation. Adding a JSDoc comment would help users understand when and how to use this parameter, especially since it enables a framework-agnostic table adapter pattern.Consider adding documentation like this:
+/** + * Creates a Vue-compatible table instance or wraps an existing table instance. + * + * @param initialOptions - Table configuration options with Vue reactive data support + * @param existingTable - Optional pre-created table instance to wrap with Vue reactivity + * @returns A table instance with Vue reactive state management + */ export function useVueTable<TData extends RowData>( initialOptions: TableOptionsWithReactiveData<TData>, existingTable?: import('@tanstack/table-core').Table<TData> ) {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
packages/angular-table/src/index.ts(2 hunks)packages/lit-table/src/index.ts(2 hunks)packages/qwik-table/src/index.tsx(2 hunks)packages/solid-table/src/index.tsx(2 hunks)packages/svelte-table/src/index.ts(2 hunks)packages/vue-table/src/index.ts(2 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-09-15T15:05:14.117Z
Learnt from: stevenwjy
Repo: TanStack/table PR: 6094
File: packages/table-core/src/utils/getGroupedRowModel.ts:74-78
Timestamp: 2025-09-15T15:05:14.117Z
Learning: In TanStack Table's Row interface, the `subRows` property is typed as `Row<TData>[]` (not optional) and the `createRow` function always initializes it as an empty array using `subRows ?? []`, so `subRows` is guaranteed to never be undefined.
Applied to files:
packages/solid-table/src/index.tsx
📚 Learning: 2025-09-15T15:05:14.117Z
Learnt from: stevenwjy
Repo: TanStack/table PR: 6094
File: packages/table-core/src/utils/getGroupedRowModel.ts:74-78
Timestamp: 2025-09-15T15:05:14.117Z
Learning: In TanStack Table's Row interface, the `subRows` property is typed as `Row<TData>[]` (not optional) and the `createRow` function always initializes it as an empty array, so `subRows` is guaranteed to never be undefined.
Applied to files:
packages/solid-table/src/index.tsx
🧬 Code graph analysis (6)
packages/angular-table/src/index.ts (3)
packages/table-core/src/types.ts (1)
TableOptions(173-177)packages/lit-table/src/index.ts (1)
table(39-67)packages/table-core/src/core/table.ts (1)
createTable(283-527)
packages/qwik-table/src/index.tsx (3)
packages/table-core/src/types.ts (1)
TableOptions(173-177)packages/table-core/src/core/table.ts (1)
createTable(283-527)packages/lit-table/src/index.ts (1)
table(39-67)
packages/lit-table/src/index.ts (2)
packages/table-core/src/types.ts (1)
TableOptions(173-177)packages/table-core/src/core/table.ts (1)
createTable(283-527)
packages/solid-table/src/index.tsx (2)
packages/table-core/src/types.ts (1)
TableOptions(173-177)packages/lit-table/src/index.ts (1)
table(39-67)
packages/svelte-table/src/index.ts (2)
packages/table-core/src/types.ts (1)
TableOptions(173-177)packages/lit-table/src/index.ts (1)
table(39-67)
packages/vue-table/src/index.ts (2)
packages/lit-table/src/index.ts (1)
table(39-67)packages/table-core/src/types.ts (1)
TableOptionsResolved(169-171)
🔇 Additional comments (13)
packages/svelte-table/src/index.ts (1)
90-90: Implementation looks good.The nullish coalescing pattern correctly reuses the provided table or creates a new one. This matches the implementation in other adapters (e.g., lit-table) and aligns with the PR objective of allowing externally created table instances to be managed by the framework adapter.
Note that the adapter will take over state management of the provided table through the
setOptionscall below (lines 97-114), initializing its internal state fromtable.initialState.packages/lit-table/src/index.ts (2)
39-39: LGTM! Signature change supports the new reuse pattern.The optional
existingTableparameter is correctly typed and maintains backward compatibility.Please verify that this new parameter is tested, especially the scenario where an external table instance is provided and properly integrated with Lit's reactive update cycle.
48-48: Correct implementation of the existingTable fallback.The nullish coalescing operator correctly uses the provided table or creates a new one. The
setOptionscall on line 55 ensures that options are properly merged and the Lit-specificonStateChangewrapper is applied regardless of table origin.packages/solid-table/src/index.tsx (1)
46-47: Table creation and state initialization logic is correct.The implementation correctly uses the
existingTablewhen provided and falls back to creating a new table otherwise. The state initialization usingtable.initialStateis appropriate for both cases and is consistent with the Lit implementation pattern shown in the relevant code snippets.packages/vue-table/src/index.ts (1)
79-81: No issues found—behavior is consistent and intentional across all framework adapters.The pattern of calling
setState()andsetOptions()after table initialization is consistent across Vue, React, Solid, Svelte, Angular, Qwik, and Lit implementations. This design allows reusing a table instance while synchronizing it with new reactive options and data—the entire purpose of theexistingTableparameter. The code is correct as written.packages/qwik-table/src/index.tsx (7)
34-35: LGTM! Clean API addition for table instance reuse.The optional
existingTableparameter follows the cross-framework pattern established in this PR and maintains backward compatibility.
46-50: Correct use of NoSerialize for Qwik's serialization model.The
useStorewithNoSerialize<Table<TData>>is the appropriate pattern for Qwik, as the Table instance contains functions and other non-serializable data that should be excluded from Qwik's resumability serialization.
58-58: Non-null assertion is safe here.The non-null assertion is justified because the previous initialization block (lines 52-56) guarantees
tableStore.instanceis set before this line executes.
61-61: LGTM! Correct state initialization.The state initialization properly uses
table.initialStatefrom either the existing or newly created table instance, and correctly leverages Qwik's signal for reactivity.
65-76: LGTM! Proper options and state management.The
setOptionscall correctly merges user options with internal state management, and theonStateChangehandler properly updates the Qwik signal while respecting user-provided callbacks. This works correctly for both new and existing table instances.
78-78: LGTM! Clean return statement.The return statement correctly provides the table instance, whether it was newly created or provided via
existingTable.
52-56: This is intentional lazy initialization—no action needed.The pattern where
existingTableis only considered on the first render is consistent across all framework adapters (Vue, Svelte, Solid, Angular, Lit, React, and Qwik). The table instance is created once and persists thereafter, maintaining a stable reference across re-renders. This is the intended design.packages/angular-table/src/index.ts (1)
40-43: State initialization withexistingTableis by design but lacks documentation.Your observation is correct: when
existingTableis provided, line 43 initializes the state signal totable.initialState, which represents the state at table creation time. Any state modifications made to the existing table after its creation will be lost.However, this pattern is intentional and consistent across all framework adapters (React, Vue, Svelte, Solid, Qwik, Lit). The design allows state to be preserved by passing it via the
options.stateparameter—line 56 mergestableOptions.stateinto the final state, enabling consumers to explicitly preserve existing state when reusing a table.The underlying issue is not a code defect but a missing contract: without documentation or tests explaining this behavior, it's unclear that consumers must explicitly pass existing state through
options.statewhen reusing a table instance. Consider:
- Adding a test case that documents the expected behavior when
existingTableis provided with modified state- Adding a JSDoc comment explaining the contract: "If reusing an existing table with modified state, pass that state via
options.state"
| export function createAngularTable<TData extends RowData>( | ||
| options: () => TableOptions<TData> | ||
| options: () => TableOptions<TData>, | ||
| existingTable?: Table<TData> | ||
| ): Table<TData> & Signal<Table<TData>> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Add JSDoc documentation for the new existingTable parameter.
The new existingTable parameter is a public API addition but lacks documentation explaining its purpose and intended usage pattern. Please add JSDoc comments describing when and how to use this parameter.
Apply this diff to add documentation:
+/**
+ * Creates an Angular-reactive table instance.
+ * @param options - A function returning table options
+ * @param existingTable - Optional existing table instance to reuse instead of creating a new one.
+ * Useful for framework-agnostic table adapters that create a table outside
+ * the Angular context and then wire it up with Angular's reactivity system.
+ * @returns A table instance that is also an Angular Signal
+ */
export function createAngularTable<TData extends RowData>(
options: () => TableOptions<TData>,
existingTable?: Table<TData>
): Table<TData> & Signal<Table<TData>> {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export function createAngularTable<TData extends RowData>( | |
| options: () => TableOptions<TData> | |
| options: () => TableOptions<TData>, | |
| existingTable?: Table<TData> | |
| ): Table<TData> & Signal<Table<TData>> { | |
| /** | |
| * Creates an Angular-reactive table instance. | |
| * @param options - A function returning table options | |
| * @param existingTable - Optional existing table instance to reuse instead of creating a new one. | |
| * Useful for framework-agnostic table adapters that create a table outside | |
| * the Angular context and then wire it up with Angular's reactivity system. | |
| * @returns A table instance that is also an Angular Signal | |
| */ | |
| export function createAngularTable<TData extends RowData>( | |
| options: () => TableOptions<TData>, | |
| existingTable?: Table<TData> | |
| ): Table<TData> & Signal<Table<TData>> { |
🤖 Prompt for AI Agents
In packages/angular-table/src/index.ts around lines 28 to 31, the public API
function createAngularTable was extended with a new existingTable parameter but
lacks JSDoc; add a JSDoc block above the function documenting the existingTable
parameter: describe that it accepts an existing Table<TData> instance to reuse
or adopt into the Angular signal wrapper, explain when to pass it (e.g., to
preserve existing table state or avoid reinitialization), clarify that it is
optional, note any side-effects (ownership/immutability expectations) and the
return type behavior, and include examples/usage notes for typical scenarios.
🎯 Changes
This enables framework agnostic table adapters to create a table outside of the react context, but then use the existing react package to interact with it as a regular table.
The second commit is AI matching the changes to
useReactTableacross all the other frameworks. I don't have the framework experience to know if it did a great job or not, but the changes should be pretty simple to review.✅ Checklist
pnpm test:pr.🚀 Release Impact
Summary by CodeRabbit
useReactTablehook with an optionalexistingTableparameter. Developers can now optionally provide an externally-created Table instance for reuse instead of having the hook automatically create a new one, providing greater flexibility and control over table instance lifecycle management.