@tanstack/react-table 9.0.0-alpha.46 → 9.0.0-alpha.48
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/dist/FlexRender.cjs.map +1 -1
- package/dist/FlexRender.js.map +1 -1
- package/dist/reactivity.cjs +1 -0
- package/dist/reactivity.cjs.map +1 -1
- package/dist/reactivity.js +1 -0
- package/dist/reactivity.js.map +1 -1
- package/package.json +6 -4
- package/skills/react/client-to-server/SKILL.md +377 -0
- package/skills/react/compose-with-tanstack-form/SKILL.md +363 -0
- package/skills/react/compose-with-tanstack-pacer/SKILL.md +287 -0
- package/skills/react/compose-with-tanstack-query/SKILL.md +467 -0
- package/skills/react/compose-with-tanstack-store/SKILL.md +347 -0
- package/skills/react/compose-with-tanstack-virtual/SKILL.md +388 -0
- package/skills/react/compose-with-tanstack-virtual/references/column-virtualization-and-infinite-scroll.md +136 -0
- package/skills/react/getting-started/SKILL.md +388 -0
- package/skills/react/migrate-v8-to-v9/SKILL.md +488 -0
- package/skills/react/production-readiness/SKILL.md +341 -0
- package/skills/react/react-subscribe-compiler-compat/SKILL.md +269 -0
- package/skills/react/table-state/SKILL.md +432 -0
- package/src/FlexRender.tsx +0 -11
- package/src/reactivity.ts +1 -0
package/README.md
CHANGED
|
@@ -49,6 +49,16 @@ A headless table library for building powerful datagrids with full control over
|
|
|
49
49
|
|
|
50
50
|
### <a href="https://tanstack.com/table">Read the Docs →</a>
|
|
51
51
|
|
|
52
|
+
## Using an AI Coding Agent?
|
|
53
|
+
|
|
54
|
+
TanStack Table ships [TanStack Intent](https://github.com/TanStack/intent) skills inside each adapter package. After installing the library, run:
|
|
55
|
+
|
|
56
|
+
```sh
|
|
57
|
+
npx @tanstack/intent@latest install
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
to add skill-loading guidance for your agent (Claude Code, Cursor, Copilot, etc.). The same CLI also exposes `intent list` to browse available skills and `intent load <skill>` to print one for inspection. Skills version with the library — your agent gets guidance that matches the version of `@tanstack/<framework>-table` you installed. Only available for v9 and above.
|
|
61
|
+
|
|
52
62
|
## Get Involved
|
|
53
63
|
|
|
54
64
|
- We welcome issues and pull requests!
|
package/dist/FlexRender.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FlexRender.cjs","names":[],"sources":["../src/FlexRender.tsx"],"sourcesContent":["import React from 'react'\nimport type {\n Cell,\n CellData,\n Header,\n RowData,\n TableFeatures,\n} from '@tanstack/table-core'\nimport type { ComponentType, JSX, ReactNode } from 'react'\n\nexport type Renderable<TProps> = ReactNode | ComponentType<TProps>\n\nfunction isReactComponent<TProps>(\n component: unknown,\n): component is ComponentType<TProps> {\n return (\n isClassComponent(component) ||\n typeof component === 'function' ||\n isExoticComponent(component)\n )\n}\n\nfunction isClassComponent(component: any) {\n return (\n typeof component === 'function' &&\n (() => {\n const proto = Object.getPrototypeOf(component)\n return proto.prototype && proto.prototype.isReactComponent\n })()\n )\n}\n\nfunction isExoticComponent(component: any) {\n return (\n typeof component === 'object' &&\n typeof component.$$typeof === 'symbol' &&\n ['react.memo', 'react.forward_ref'].includes(component.$$typeof.description)\n )\n}\n\n/**\n * If rendering headers, cells, or footers with custom markup, use flexRender instead of `cell.getValue()` or `cell.renderValue()`.\n * @example flexRender(cell.column.columnDef.cell, cell.getContext())\n */\nexport function flexRender<TProps extends object>(\n Comp: Renderable<TProps>,\n props: TProps,\n): ReactNode | JSX.Element {\n return !Comp ? null : isReactComponent<TProps>(Comp) ? (\n <Comp {...props} />\n ) : (\n Comp\n )\n}\n\n/**\n * Simplified component wrapper of `flexRender`. Use this utility component to render headers, cells, or footers with custom markup.\n * Only one prop (`cell`, `header`, or `footer`) may be passed.\n * @example <FlexRender cell={cell} />\n * @example <FlexRender header={header} />\n * @example <FlexRender footer={footer} />\n */\nexport type FlexRenderProps<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n> =\n | { cell: Cell<TFeatures, TData, TValue>; header?: never; footer?: never }\n | {\n header: Header<TFeatures, TData, TValue>\n cell?: never\n footer?: never\n }\n | {\n footer: Header<TFeatures, TData, TValue>\n cell?: never\n header?: never\n }\n\n/**\n * Simplified component wrapper of `flexRender`. Use this utility component to render headers, cells, or footers with custom markup.\n * Only one prop (`cell`, `header`, or `footer`) may be passed.\n * @example\n * ```tsx\n * <FlexRender cell={cell} />\n * <FlexRender header={header} />\n * <FlexRender footer={footer} />\n * ```\n *\n * This replaces calling `flexRender` directly like this:\n * ```tsx\n * flexRender(cell.column.columnDef.cell, cell.getContext())\n * flexRender(header.column.columnDef.header, header.getContext())\n * flexRender(footer.column.columnDef.footer, footer.getContext())\n * ```\n */\nexport function FlexRender<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(props: FlexRenderProps<TFeatures, TData, TValue>) {\n if ('cell' in props && props.cell) {\n const cell = props.cell\n const def = cell.column.columnDef\n
|
|
1
|
+
{"version":3,"file":"FlexRender.cjs","names":[],"sources":["../src/FlexRender.tsx"],"sourcesContent":["import React from 'react'\nimport type {\n Cell,\n CellData,\n Header,\n RowData,\n TableFeatures,\n} from '@tanstack/table-core'\nimport type { ComponentType, JSX, ReactNode } from 'react'\n\nexport type Renderable<TProps> = ReactNode | ComponentType<TProps>\n\nfunction isReactComponent<TProps>(\n component: unknown,\n): component is ComponentType<TProps> {\n return (\n isClassComponent(component) ||\n typeof component === 'function' ||\n isExoticComponent(component)\n )\n}\n\nfunction isClassComponent(component: any) {\n return (\n typeof component === 'function' &&\n (() => {\n const proto = Object.getPrototypeOf(component)\n return proto.prototype && proto.prototype.isReactComponent\n })()\n )\n}\n\nfunction isExoticComponent(component: any) {\n return (\n typeof component === 'object' &&\n typeof component.$$typeof === 'symbol' &&\n ['react.memo', 'react.forward_ref'].includes(component.$$typeof.description)\n )\n}\n\n/**\n * If rendering headers, cells, or footers with custom markup, use flexRender instead of `cell.getValue()` or `cell.renderValue()`.\n * @example flexRender(cell.column.columnDef.cell, cell.getContext())\n */\nexport function flexRender<TProps extends object>(\n Comp: Renderable<TProps>,\n props: TProps,\n): ReactNode | JSX.Element {\n return !Comp ? null : isReactComponent<TProps>(Comp) ? (\n <Comp {...props} />\n ) : (\n Comp\n )\n}\n\n/**\n * Simplified component wrapper of `flexRender`. Use this utility component to render headers, cells, or footers with custom markup.\n * Only one prop (`cell`, `header`, or `footer`) may be passed.\n * @example <FlexRender cell={cell} />\n * @example <FlexRender header={header} />\n * @example <FlexRender footer={footer} />\n */\nexport type FlexRenderProps<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n> =\n | { cell: Cell<TFeatures, TData, TValue>; header?: never; footer?: never }\n | {\n header: Header<TFeatures, TData, TValue>\n cell?: never\n footer?: never\n }\n | {\n footer: Header<TFeatures, TData, TValue>\n cell?: never\n header?: never\n }\n\n/**\n * Simplified component wrapper of `flexRender`. Use this utility component to render headers, cells, or footers with custom markup.\n * Only one prop (`cell`, `header`, or `footer`) may be passed.\n * @example\n * ```tsx\n * <FlexRender cell={cell} />\n * <FlexRender header={header} />\n * <FlexRender footer={footer} />\n * ```\n *\n * This replaces calling `flexRender` directly like this:\n * ```tsx\n * flexRender(cell.column.columnDef.cell, cell.getContext())\n * flexRender(header.column.columnDef.header, header.getContext())\n * flexRender(footer.column.columnDef.footer, footer.getContext())\n * ```\n */\nexport function FlexRender<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(props: FlexRenderProps<TFeatures, TData, TValue>) {\n if ('cell' in props && props.cell) {\n const cell = props.cell\n const def = cell.column.columnDef\n const groupingCell = cell as typeof cell & {\n getIsAggregated?: () => boolean\n getIsPlaceholder?: () => boolean\n }\n const groupingDef = def as typeof def & {\n aggregatedCell?: typeof def.cell\n }\n if (groupingCell.getIsAggregated?.()) {\n return flexRender(\n groupingDef.aggregatedCell ?? def.cell,\n cell.getContext(),\n )\n }\n if (groupingCell.getIsPlaceholder?.()) {\n return null\n }\n return flexRender(def.cell, cell.getContext())\n }\n if ('header' in props && props.header) {\n return flexRender(\n props.header.column.columnDef.header,\n props.header.getContext(),\n )\n }\n if ('footer' in props && props.footer) {\n return flexRender(\n props.footer.column.columnDef.footer,\n props.footer.getContext(),\n )\n }\n return null\n}\n"],"mappings":";;;;;AAYA,SAAS,iBACP,WACoC;CACpC,OACE,iBAAiB,UAAU,IAC3B,OAAO,cAAc,cACrB,kBAAkB,UAAU;;AAIhC,SAAS,iBAAiB,WAAgB;CACxC,OACE,OAAO,cAAc,qBACd;EACL,MAAM,QAAQ,OAAO,eAAe,UAAU;EAC9C,OAAO,MAAM,aAAa,MAAM,UAAU;KACxC;;AAIR,SAAS,kBAAkB,WAAgB;CACzC,OACE,OAAO,cAAc,YACrB,OAAO,UAAU,aAAa,YAC9B,CAAC,cAAc,oBAAoB,CAAC,SAAS,UAAU,SAAS,YAAY;;;;;;AAQhF,SAAgB,WACd,MACA,OACyB;CACzB,OAAO,CAAC,OAAO,OAAO,iBAAyB,KAAK,GAClD,4CAAC,MAAS,MAAS,GAEnB;;;;;;;;;;;;;;;;;;;AA6CJ,SAAgB,WAId,OAAkD;CAClD,IAAI,UAAU,SAAS,MAAM,MAAM;;EACjC,MAAM,OAAO,MAAM;EACnB,MAAM,MAAM,KAAK,OAAO;EACxB,MAAM,eAAe;EAIrB,MAAM,cAAc;EAGpB,6BAAI,aAAa,iHAAmB,EAClC,OAAO,WACL,YAAY,kBAAkB,IAAI,MAClC,KAAK,YAAY,CAClB;EAEH,6BAAI,aAAa,kHAAoB,EACnC,OAAO;EAET,OAAO,WAAW,IAAI,MAAM,KAAK,YAAY,CAAC;;CAEhD,IAAI,YAAY,SAAS,MAAM,QAC7B,OAAO,WACL,MAAM,OAAO,OAAO,UAAU,QAC9B,MAAM,OAAO,YAAY,CAC1B;CAEH,IAAI,YAAY,SAAS,MAAM,QAC7B,OAAO,WACL,MAAM,OAAO,OAAO,UAAU,QAC9B,MAAM,OAAO,YAAY,CAC1B;CAEH,OAAO"}
|
package/dist/FlexRender.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FlexRender.js","names":[],"sources":["../src/FlexRender.tsx"],"sourcesContent":["import React from 'react'\nimport type {\n Cell,\n CellData,\n Header,\n RowData,\n TableFeatures,\n} from '@tanstack/table-core'\nimport type { ComponentType, JSX, ReactNode } from 'react'\n\nexport type Renderable<TProps> = ReactNode | ComponentType<TProps>\n\nfunction isReactComponent<TProps>(\n component: unknown,\n): component is ComponentType<TProps> {\n return (\n isClassComponent(component) ||\n typeof component === 'function' ||\n isExoticComponent(component)\n )\n}\n\nfunction isClassComponent(component: any) {\n return (\n typeof component === 'function' &&\n (() => {\n const proto = Object.getPrototypeOf(component)\n return proto.prototype && proto.prototype.isReactComponent\n })()\n )\n}\n\nfunction isExoticComponent(component: any) {\n return (\n typeof component === 'object' &&\n typeof component.$$typeof === 'symbol' &&\n ['react.memo', 'react.forward_ref'].includes(component.$$typeof.description)\n )\n}\n\n/**\n * If rendering headers, cells, or footers with custom markup, use flexRender instead of `cell.getValue()` or `cell.renderValue()`.\n * @example flexRender(cell.column.columnDef.cell, cell.getContext())\n */\nexport function flexRender<TProps extends object>(\n Comp: Renderable<TProps>,\n props: TProps,\n): ReactNode | JSX.Element {\n return !Comp ? null : isReactComponent<TProps>(Comp) ? (\n <Comp {...props} />\n ) : (\n Comp\n )\n}\n\n/**\n * Simplified component wrapper of `flexRender`. Use this utility component to render headers, cells, or footers with custom markup.\n * Only one prop (`cell`, `header`, or `footer`) may be passed.\n * @example <FlexRender cell={cell} />\n * @example <FlexRender header={header} />\n * @example <FlexRender footer={footer} />\n */\nexport type FlexRenderProps<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n> =\n | { cell: Cell<TFeatures, TData, TValue>; header?: never; footer?: never }\n | {\n header: Header<TFeatures, TData, TValue>\n cell?: never\n footer?: never\n }\n | {\n footer: Header<TFeatures, TData, TValue>\n cell?: never\n header?: never\n }\n\n/**\n * Simplified component wrapper of `flexRender`. Use this utility component to render headers, cells, or footers with custom markup.\n * Only one prop (`cell`, `header`, or `footer`) may be passed.\n * @example\n * ```tsx\n * <FlexRender cell={cell} />\n * <FlexRender header={header} />\n * <FlexRender footer={footer} />\n * ```\n *\n * This replaces calling `flexRender` directly like this:\n * ```tsx\n * flexRender(cell.column.columnDef.cell, cell.getContext())\n * flexRender(header.column.columnDef.header, header.getContext())\n * flexRender(footer.column.columnDef.footer, footer.getContext())\n * ```\n */\nexport function FlexRender<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(props: FlexRenderProps<TFeatures, TData, TValue>) {\n if ('cell' in props && props.cell) {\n const cell = props.cell\n const def = cell.column.columnDef\n
|
|
1
|
+
{"version":3,"file":"FlexRender.js","names":[],"sources":["../src/FlexRender.tsx"],"sourcesContent":["import React from 'react'\nimport type {\n Cell,\n CellData,\n Header,\n RowData,\n TableFeatures,\n} from '@tanstack/table-core'\nimport type { ComponentType, JSX, ReactNode } from 'react'\n\nexport type Renderable<TProps> = ReactNode | ComponentType<TProps>\n\nfunction isReactComponent<TProps>(\n component: unknown,\n): component is ComponentType<TProps> {\n return (\n isClassComponent(component) ||\n typeof component === 'function' ||\n isExoticComponent(component)\n )\n}\n\nfunction isClassComponent(component: any) {\n return (\n typeof component === 'function' &&\n (() => {\n const proto = Object.getPrototypeOf(component)\n return proto.prototype && proto.prototype.isReactComponent\n })()\n )\n}\n\nfunction isExoticComponent(component: any) {\n return (\n typeof component === 'object' &&\n typeof component.$$typeof === 'symbol' &&\n ['react.memo', 'react.forward_ref'].includes(component.$$typeof.description)\n )\n}\n\n/**\n * If rendering headers, cells, or footers with custom markup, use flexRender instead of `cell.getValue()` or `cell.renderValue()`.\n * @example flexRender(cell.column.columnDef.cell, cell.getContext())\n */\nexport function flexRender<TProps extends object>(\n Comp: Renderable<TProps>,\n props: TProps,\n): ReactNode | JSX.Element {\n return !Comp ? null : isReactComponent<TProps>(Comp) ? (\n <Comp {...props} />\n ) : (\n Comp\n )\n}\n\n/**\n * Simplified component wrapper of `flexRender`. Use this utility component to render headers, cells, or footers with custom markup.\n * Only one prop (`cell`, `header`, or `footer`) may be passed.\n * @example <FlexRender cell={cell} />\n * @example <FlexRender header={header} />\n * @example <FlexRender footer={footer} />\n */\nexport type FlexRenderProps<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n> =\n | { cell: Cell<TFeatures, TData, TValue>; header?: never; footer?: never }\n | {\n header: Header<TFeatures, TData, TValue>\n cell?: never\n footer?: never\n }\n | {\n footer: Header<TFeatures, TData, TValue>\n cell?: never\n header?: never\n }\n\n/**\n * Simplified component wrapper of `flexRender`. Use this utility component to render headers, cells, or footers with custom markup.\n * Only one prop (`cell`, `header`, or `footer`) may be passed.\n * @example\n * ```tsx\n * <FlexRender cell={cell} />\n * <FlexRender header={header} />\n * <FlexRender footer={footer} />\n * ```\n *\n * This replaces calling `flexRender` directly like this:\n * ```tsx\n * flexRender(cell.column.columnDef.cell, cell.getContext())\n * flexRender(header.column.columnDef.header, header.getContext())\n * flexRender(footer.column.columnDef.footer, footer.getContext())\n * ```\n */\nexport function FlexRender<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(props: FlexRenderProps<TFeatures, TData, TValue>) {\n if ('cell' in props && props.cell) {\n const cell = props.cell\n const def = cell.column.columnDef\n const groupingCell = cell as typeof cell & {\n getIsAggregated?: () => boolean\n getIsPlaceholder?: () => boolean\n }\n const groupingDef = def as typeof def & {\n aggregatedCell?: typeof def.cell\n }\n if (groupingCell.getIsAggregated?.()) {\n return flexRender(\n groupingDef.aggregatedCell ?? def.cell,\n cell.getContext(),\n )\n }\n if (groupingCell.getIsPlaceholder?.()) {\n return null\n }\n return flexRender(def.cell, cell.getContext())\n }\n if ('header' in props && props.header) {\n return flexRender(\n props.header.column.columnDef.header,\n props.header.getContext(),\n )\n }\n if ('footer' in props && props.footer) {\n return flexRender(\n props.footer.column.columnDef.footer,\n props.footer.getContext(),\n )\n }\n return null\n}\n"],"mappings":";;;AAYA,SAAS,iBACP,WACoC;CACpC,OACE,iBAAiB,UAAU,IAC3B,OAAO,cAAc,cACrB,kBAAkB,UAAU;;AAIhC,SAAS,iBAAiB,WAAgB;CACxC,OACE,OAAO,cAAc,qBACd;EACL,MAAM,QAAQ,OAAO,eAAe,UAAU;EAC9C,OAAO,MAAM,aAAa,MAAM,UAAU;KACxC;;AAIR,SAAS,kBAAkB,WAAgB;CACzC,OACE,OAAO,cAAc,YACrB,OAAO,UAAU,aAAa,YAC9B,CAAC,cAAc,oBAAoB,CAAC,SAAS,UAAU,SAAS,YAAY;;;;;;AAQhF,SAAgB,WACd,MACA,OACyB;CACzB,OAAO,CAAC,OAAO,OAAO,iBAAyB,KAAK,GAClD,oCAAC,MAAS,MAAS,GAEnB;;;;;;;;;;;;;;;;;;;AA6CJ,SAAgB,WAId,OAAkD;CAClD,IAAI,UAAU,SAAS,MAAM,MAAM;;EACjC,MAAM,OAAO,MAAM;EACnB,MAAM,MAAM,KAAK,OAAO;EACxB,MAAM,eAAe;EAIrB,MAAM,cAAc;EAGpB,6BAAI,aAAa,iHAAmB,EAClC,OAAO,WACL,YAAY,kBAAkB,IAAI,MAClC,KAAK,YAAY,CAClB;EAEH,6BAAI,aAAa,kHAAoB,EACnC,OAAO;EAET,OAAO,WAAW,IAAI,MAAM,KAAK,YAAY,CAAC;;CAEhD,IAAI,YAAY,SAAS,MAAM,QAC7B,OAAO,WACL,MAAM,OAAO,OAAO,UAAU,QAC9B,MAAM,OAAO,YAAY,CAC1B;CAEH,IAAI,YAAY,SAAS,MAAM,QAC7B,OAAO,WACL,MAAM,OAAO,OAAO,UAAU,QAC9B,MAAM,OAAO,YAAY,CAC1B;CAEH,OAAO"}
|
package/dist/reactivity.cjs
CHANGED
|
@@ -11,6 +11,7 @@ let _tanstack_react_store = require("@tanstack/react-store");
|
|
|
11
11
|
function reactReactivity() {
|
|
12
12
|
return {
|
|
13
13
|
createOptionsStore: false,
|
|
14
|
+
schedule: (fn) => queueMicrotask(fn),
|
|
14
15
|
batch: _tanstack_react_store.batch,
|
|
15
16
|
untrack: (fn) => fn(),
|
|
16
17
|
createReadonlyAtom: (fn, options) => {
|
package/dist/reactivity.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reactivity.cjs","names":[],"sources":["../src/reactivity.ts"],"sourcesContent":["import { batch, createAtom } from '@tanstack/react-store'\nimport type {\n TableAtomOptions,\n TableReactivityBindings,\n} from '@tanstack/table-core/reactivity'\n\n/**\n * Creates the table-core reactivity bindings used by the React adapter.\n *\n * React stores table state in TanStack Store atoms and leaves options as plain\n * resolved data because `useTable` synchronizes options during render.\n */\nexport function reactReactivity(): TableReactivityBindings {\n return {\n createOptionsStore: false,\n batch,\n untrack: (fn) => fn(),\n createReadonlyAtom: <T>(fn: () => T, options?: TableAtomOptions<T>) => {\n return createAtom(() => fn(), {\n compare: options?.compare,\n })\n },\n createWritableAtom: <T>(value: T, options?: TableAtomOptions<T>) => {\n return createAtom(value, {\n compare: options?.compare,\n })\n },\n }\n}\n"],"mappings":";;;;;;;;;;AAYA,SAAgB,kBAA2C;CACzD,OAAO;EACL,oBAAoB;EACpB;EACA,UAAU,OAAO,IAAI;EACrB,qBAAwB,IAAa,YAAkC;GACrE,mDAAwB,IAAI,EAAE,EAC5B,2DAAS,QAAS,SACnB,CAAC;;EAEJ,qBAAwB,OAAU,YAAkC;GAClE,6CAAkB,OAAO,EACvB,2DAAS,QAAS,SACnB,CAAC;;EAEL"}
|
|
1
|
+
{"version":3,"file":"reactivity.cjs","names":[],"sources":["../src/reactivity.ts"],"sourcesContent":["import { batch, createAtom } from '@tanstack/react-store'\nimport type {\n TableAtomOptions,\n TableReactivityBindings,\n} from '@tanstack/table-core/reactivity'\n\n/**\n * Creates the table-core reactivity bindings used by the React adapter.\n *\n * React stores table state in TanStack Store atoms and leaves options as plain\n * resolved data because `useTable` synchronizes options during render.\n */\nexport function reactReactivity(): TableReactivityBindings {\n return {\n createOptionsStore: false,\n schedule: (fn) => queueMicrotask(fn),\n batch,\n untrack: (fn) => fn(),\n createReadonlyAtom: <T>(fn: () => T, options?: TableAtomOptions<T>) => {\n return createAtom(() => fn(), {\n compare: options?.compare,\n })\n },\n createWritableAtom: <T>(value: T, options?: TableAtomOptions<T>) => {\n return createAtom(value, {\n compare: options?.compare,\n })\n },\n }\n}\n"],"mappings":";;;;;;;;;;AAYA,SAAgB,kBAA2C;CACzD,OAAO;EACL,oBAAoB;EACpB,WAAW,OAAO,eAAe,GAAG;EACpC;EACA,UAAU,OAAO,IAAI;EACrB,qBAAwB,IAAa,YAAkC;GACrE,mDAAwB,IAAI,EAAE,EAC5B,2DAAS,QAAS,SACnB,CAAC;;EAEJ,qBAAwB,OAAU,YAAkC;GAClE,6CAAkB,OAAO,EACvB,2DAAS,QAAS,SACnB,CAAC;;EAEL"}
|
package/dist/reactivity.js
CHANGED
package/dist/reactivity.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reactivity.js","names":[],"sources":["../src/reactivity.ts"],"sourcesContent":["import { batch, createAtom } from '@tanstack/react-store'\nimport type {\n TableAtomOptions,\n TableReactivityBindings,\n} from '@tanstack/table-core/reactivity'\n\n/**\n * Creates the table-core reactivity bindings used by the React adapter.\n *\n * React stores table state in TanStack Store atoms and leaves options as plain\n * resolved data because `useTable` synchronizes options during render.\n */\nexport function reactReactivity(): TableReactivityBindings {\n return {\n createOptionsStore: false,\n batch,\n untrack: (fn) => fn(),\n createReadonlyAtom: <T>(fn: () => T, options?: TableAtomOptions<T>) => {\n return createAtom(() => fn(), {\n compare: options?.compare,\n })\n },\n createWritableAtom: <T>(value: T, options?: TableAtomOptions<T>) => {\n return createAtom(value, {\n compare: options?.compare,\n })\n },\n }\n}\n"],"mappings":";;;;;;;;;AAYA,SAAgB,kBAA2C;CACzD,OAAO;EACL,oBAAoB;EACpB;EACA,UAAU,OAAO,IAAI;EACrB,qBAAwB,IAAa,YAAkC;GACrE,OAAO,iBAAiB,IAAI,EAAE,EAC5B,2DAAS,QAAS,SACnB,CAAC;;EAEJ,qBAAwB,OAAU,YAAkC;GAClE,OAAO,WAAW,OAAO,EACvB,2DAAS,QAAS,SACnB,CAAC;;EAEL"}
|
|
1
|
+
{"version":3,"file":"reactivity.js","names":[],"sources":["../src/reactivity.ts"],"sourcesContent":["import { batch, createAtom } from '@tanstack/react-store'\nimport type {\n TableAtomOptions,\n TableReactivityBindings,\n} from '@tanstack/table-core/reactivity'\n\n/**\n * Creates the table-core reactivity bindings used by the React adapter.\n *\n * React stores table state in TanStack Store atoms and leaves options as plain\n * resolved data because `useTable` synchronizes options during render.\n */\nexport function reactReactivity(): TableReactivityBindings {\n return {\n createOptionsStore: false,\n schedule: (fn) => queueMicrotask(fn),\n batch,\n untrack: (fn) => fn(),\n createReadonlyAtom: <T>(fn: () => T, options?: TableAtomOptions<T>) => {\n return createAtom(() => fn(), {\n compare: options?.compare,\n })\n },\n createWritableAtom: <T>(value: T, options?: TableAtomOptions<T>) => {\n return createAtom(value, {\n compare: options?.compare,\n })\n },\n }\n}\n"],"mappings":";;;;;;;;;AAYA,SAAgB,kBAA2C;CACzD,OAAO;EACL,oBAAoB;EACpB,WAAW,OAAO,eAAe,GAAG;EACpC;EACA,UAAU,OAAO,IAAI;EACrB,qBAAwB,IAAa,YAAkC;GACrE,OAAO,iBAAiB,IAAI,EAAE,EAC5B,2DAAS,QAAS,SACnB,CAAC;;EAEJ,qBAAwB,OAAU,YAAkC;GAClE,OAAO,WAAW,OAAO,EACvB,2DAAS,QAAS,SACnB,CAAC;;EAEL"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/react-table",
|
|
3
|
-
"version": "9.0.0-alpha.
|
|
3
|
+
"version": "9.0.0-alpha.48",
|
|
4
4
|
"description": "Headless UI for building powerful tables & datagrids for React.",
|
|
5
5
|
"author": "Tanner Linsley",
|
|
6
6
|
"license": "MIT",
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
"react",
|
|
19
19
|
"table",
|
|
20
20
|
"react-table",
|
|
21
|
-
"datagrid"
|
|
21
|
+
"datagrid",
|
|
22
|
+
"tanstack-intent"
|
|
22
23
|
],
|
|
23
24
|
"type": "module",
|
|
24
25
|
"types": "./dist/index.d.cts",
|
|
@@ -49,11 +50,12 @@
|
|
|
49
50
|
},
|
|
50
51
|
"files": [
|
|
51
52
|
"dist",
|
|
52
|
-
"src"
|
|
53
|
+
"src",
|
|
54
|
+
"skills"
|
|
53
55
|
],
|
|
54
56
|
"dependencies": {
|
|
55
57
|
"@tanstack/react-store": "^0.11.0",
|
|
56
|
-
"@tanstack/table-core": "9.0.0-alpha.
|
|
58
|
+
"@tanstack/table-core": "9.0.0-alpha.48"
|
|
57
59
|
},
|
|
58
60
|
"devDependencies": {
|
|
59
61
|
"@eslint-react/eslint-plugin": "^5.7.7",
|
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: react/client-to-server
|
|
3
|
+
description: >
|
|
4
|
+
Convert a client-side `@tanstack/react-table` v9 table to server-side
|
|
5
|
+
(manual modes). Pass server-paginated/sorted/filtered rows as `data`, set
|
|
6
|
+
`manualPagination` / `manualSorting` / `manualFiltering` / `manualGrouping` /
|
|
7
|
+
`manualExpanding` for whatever the server now owns, supply `rowCount` so
|
|
8
|
+
`getPageCount()` works, and DROP the matching `_rowModels` entry (no
|
|
9
|
+
`paginatedRowModel` if the server paginates). Own the relevant state slices
|
|
10
|
+
via external atoms (`useCreateAtom` + `options.atoms`) so a query can key on
|
|
11
|
+
the slice and refetch automatically — OR via classic `state` + `on*Change`
|
|
12
|
+
controlled state.
|
|
13
|
+
type: lifecycle
|
|
14
|
+
library: tanstack-table
|
|
15
|
+
framework: react
|
|
16
|
+
library_version: '9.0.0-alpha.47'
|
|
17
|
+
requires:
|
|
18
|
+
- state-management
|
|
19
|
+
- pagination
|
|
20
|
+
- filtering
|
|
21
|
+
- sorting
|
|
22
|
+
- react/table-state
|
|
23
|
+
sources:
|
|
24
|
+
- TanStack/table:examples/react/basic-external-atoms/src/main.tsx
|
|
25
|
+
- TanStack/table:examples/react/with-tanstack-query/src/main.tsx
|
|
26
|
+
- TanStack/table:examples/react/with-tanstack-query/src/fetchData.ts
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
This skill builds on `tanstack-table/state-management` and `tanstack-table/react/table-state`. Read those first — the atom model is what makes the cleanest server-side wiring possible.
|
|
30
|
+
|
|
31
|
+
## Why "client-to-server"
|
|
32
|
+
|
|
33
|
+
A client-side table sees every row, sorts/filters/paginates them locally, and renders a slice. A server-side table sees only the slice the server returned for the current request; the table must be told "don't try to slice this again — and here's the total row count so you can render a pager".
|
|
34
|
+
|
|
35
|
+
Four moves convert any client table to a server table:
|
|
36
|
+
|
|
37
|
+
1. **`manualX: true`** for whichever operations the server owns.
|
|
38
|
+
2. **Drop the matching factory** from `_rowModels` so it doesn't ship in your bundle.
|
|
39
|
+
3. **Provide `rowCount`** so `table.getPageCount()` / `getCanNextPage()` work.
|
|
40
|
+
4. **Own the slice state externally** so your data fetcher can key on it.
|
|
41
|
+
|
|
42
|
+
## Setup
|
|
43
|
+
|
|
44
|
+
Two state-ownership patterns work; pick one per slice.
|
|
45
|
+
|
|
46
|
+
### Pattern A — external atom (cleanest with Query/SWR)
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
import * as React from 'react'
|
|
50
|
+
import { useCreateAtom, useSelector } from '@tanstack/react-store'
|
|
51
|
+
import {
|
|
52
|
+
useTable,
|
|
53
|
+
tableFeatures,
|
|
54
|
+
rowPaginationFeature,
|
|
55
|
+
createColumnHelper,
|
|
56
|
+
} from '@tanstack/react-table'
|
|
57
|
+
import type { PaginationState } from '@tanstack/react-table'
|
|
58
|
+
|
|
59
|
+
const _features = tableFeatures({ rowPaginationFeature })
|
|
60
|
+
const columnHelper = createColumnHelper<typeof _features, Person>()
|
|
61
|
+
const columns = columnHelper.columns([
|
|
62
|
+
columnHelper.accessor('firstName', { header: 'First' }),
|
|
63
|
+
columnHelper.accessor('age', { header: 'Age' }),
|
|
64
|
+
])
|
|
65
|
+
|
|
66
|
+
const EMPTY: Person[] = []
|
|
67
|
+
|
|
68
|
+
function ServerTable() {
|
|
69
|
+
// 1) Own pagination in an external atom.
|
|
70
|
+
const paginationAtom = useCreateAtom<PaginationState>({
|
|
71
|
+
pageIndex: 0,
|
|
72
|
+
pageSize: 10,
|
|
73
|
+
})
|
|
74
|
+
const pagination = useSelector(paginationAtom)
|
|
75
|
+
|
|
76
|
+
// 2) Fetch keyed on the atom value.
|
|
77
|
+
const [serverPage, setServerPage] = React.useState<{
|
|
78
|
+
rows: Person[]
|
|
79
|
+
rowCount: number
|
|
80
|
+
} | null>(null)
|
|
81
|
+
React.useEffect(() => {
|
|
82
|
+
let cancelled = false
|
|
83
|
+
fetchPeople(pagination).then((page) => {
|
|
84
|
+
if (!cancelled) setServerPage(page)
|
|
85
|
+
})
|
|
86
|
+
return () => {
|
|
87
|
+
cancelled = true
|
|
88
|
+
}
|
|
89
|
+
}, [pagination])
|
|
90
|
+
|
|
91
|
+
// 3) Manual pagination + rowCount. No paginatedRowModel in _rowModels.
|
|
92
|
+
const table = useTable({
|
|
93
|
+
_features,
|
|
94
|
+
_rowModels: {}, // core only — server slices
|
|
95
|
+
columns,
|
|
96
|
+
data: serverPage?.rows ?? EMPTY, // EMPTY at module scope
|
|
97
|
+
rowCount: serverPage?.rowCount,
|
|
98
|
+
atoms: { pagination: paginationAtom }, // table writes here directly
|
|
99
|
+
manualPagination: true,
|
|
100
|
+
})
|
|
101
|
+
// No onPaginationChange — table.setPageIndex(...) writes through the atom.
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Source: `examples/react/basic-external-atoms/src/main.tsx` (atoms wiring); `examples/react/with-tanstack-query/src/main.tsx` (rowCount + manualPagination).
|
|
106
|
+
|
|
107
|
+
### Pattern B — classic `state` + `on*Change`
|
|
108
|
+
|
|
109
|
+
```tsx
|
|
110
|
+
const [pagination, setPagination] = React.useState<PaginationState>({
|
|
111
|
+
pageIndex: 0,
|
|
112
|
+
pageSize: 10,
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
const table = useTable({
|
|
116
|
+
_features,
|
|
117
|
+
_rowModels: {},
|
|
118
|
+
columns,
|
|
119
|
+
data: serverPage?.rows ?? EMPTY,
|
|
120
|
+
rowCount: serverPage?.rowCount,
|
|
121
|
+
state: { pagination },
|
|
122
|
+
onPaginationChange: setPagination, // REQUIRED with state.pagination
|
|
123
|
+
manualPagination: true,
|
|
124
|
+
})
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Both work. `state` + `on*Change` is familiar from v8; atoms compose more cleanly with Query (the table writes to the atom, the query key includes the atom value, the query refetches automatically).
|
|
128
|
+
|
|
129
|
+
## Core Patterns
|
|
130
|
+
|
|
131
|
+
### Combining server-side sort + filter + pagination
|
|
132
|
+
|
|
133
|
+
Add the matching `manual*` flags for each operation the server now owns. Local features (column visibility, ordering, pinning) still work because they don't depend on the row model.
|
|
134
|
+
|
|
135
|
+
```tsx
|
|
136
|
+
const _features = tableFeatures({
|
|
137
|
+
rowSortingFeature,
|
|
138
|
+
rowPaginationFeature,
|
|
139
|
+
columnFilteringFeature,
|
|
140
|
+
columnVisibilityFeature, // still local
|
|
141
|
+
columnPinningFeature, // still local
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
const sortingAtom = useCreateAtom<SortingState>([])
|
|
145
|
+
const paginationAtom = useCreateAtom<PaginationState>({
|
|
146
|
+
pageIndex: 0,
|
|
147
|
+
pageSize: 10,
|
|
148
|
+
})
|
|
149
|
+
const columnFiltersAtom = useCreateAtom<ColumnFiltersState>([])
|
|
150
|
+
|
|
151
|
+
const sorting = useSelector(sortingAtom)
|
|
152
|
+
const pagination = useSelector(paginationAtom)
|
|
153
|
+
const columnFilters = useSelector(columnFiltersAtom)
|
|
154
|
+
|
|
155
|
+
const serverArgs = { sorting, pagination, columnFilters }
|
|
156
|
+
// ... fetch keyed on serverArgs
|
|
157
|
+
|
|
158
|
+
const table = useTable({
|
|
159
|
+
_features,
|
|
160
|
+
_rowModels: {}, // no sorted/filtered/paginated factories — server owns them
|
|
161
|
+
columns,
|
|
162
|
+
data: serverPage?.rows ?? EMPTY,
|
|
163
|
+
rowCount: serverPage?.rowCount,
|
|
164
|
+
atoms: {
|
|
165
|
+
sorting: sortingAtom,
|
|
166
|
+
pagination: paginationAtom,
|
|
167
|
+
columnFilters: columnFiltersAtom,
|
|
168
|
+
},
|
|
169
|
+
manualSorting: true,
|
|
170
|
+
manualFiltering: true,
|
|
171
|
+
manualPagination: true,
|
|
172
|
+
})
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Source: `examples/react/basic-external-atoms/src/main.tsx`.
|
|
176
|
+
|
|
177
|
+
### When NOT to manual-mode a slice
|
|
178
|
+
|
|
179
|
+
If the server returns the **entire** dataset, leave the table client-side. Manual mode is for slices the server has already trimmed.
|
|
180
|
+
|
|
181
|
+
## Common Mistakes
|
|
182
|
+
|
|
183
|
+
### CRITICAL Forgetting `manualPagination` / `manualSorting` / `manualFiltering`
|
|
184
|
+
|
|
185
|
+
Wrong:
|
|
186
|
+
|
|
187
|
+
```tsx
|
|
188
|
+
const table = useTable({
|
|
189
|
+
_features,
|
|
190
|
+
_rowModels: { paginatedRowModel: createPaginatedRowModel() },
|
|
191
|
+
columns,
|
|
192
|
+
data: serverPage.rows,
|
|
193
|
+
// missing manualPagination
|
|
194
|
+
})
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Correct:
|
|
198
|
+
|
|
199
|
+
```tsx
|
|
200
|
+
const table = useTable({
|
|
201
|
+
_features,
|
|
202
|
+
_rowModels: {}, // dropped — server paginates
|
|
203
|
+
columns,
|
|
204
|
+
data: serverPage.rows,
|
|
205
|
+
rowCount: serverPage.rowCount,
|
|
206
|
+
manualPagination: true,
|
|
207
|
+
})
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Without `manualPagination: true`, the table tries to slice the already-server-sliced page a second time, producing rows that don't exist (or visibly wrong pagination).
|
|
211
|
+
Source: `examples/react/with-tanstack-query/src/main.tsx`.
|
|
212
|
+
|
|
213
|
+
### CRITICAL Missing `rowCount`
|
|
214
|
+
|
|
215
|
+
Wrong:
|
|
216
|
+
|
|
217
|
+
```tsx
|
|
218
|
+
const table = useTable({
|
|
219
|
+
_features,
|
|
220
|
+
_rowModels: {},
|
|
221
|
+
columns,
|
|
222
|
+
data: serverPage.rows,
|
|
223
|
+
manualPagination: true,
|
|
224
|
+
// missing rowCount: serverPage.totalRowCount
|
|
225
|
+
})
|
|
226
|
+
// table.getPageCount() → 1, pager locks at "Page 1 of 1"
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Correct:
|
|
230
|
+
|
|
231
|
+
```tsx
|
|
232
|
+
const table = useTable({
|
|
233
|
+
_features,
|
|
234
|
+
_rowModels: {},
|
|
235
|
+
columns,
|
|
236
|
+
data: serverPage.rows,
|
|
237
|
+
rowCount: serverPage.rowCount, // ← required for accurate pager
|
|
238
|
+
manualPagination: true,
|
|
239
|
+
})
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
Without `rowCount`, `getPageCount()` falls back to `Math.ceil(data.length / pageSize)` — which is 1 if the server returned a single page.
|
|
243
|
+
Source: `examples/react/with-tanstack-query/src/main.tsx`.
|
|
244
|
+
|
|
245
|
+
### HIGH `state.pagination` without `onPaginationChange`
|
|
246
|
+
|
|
247
|
+
Wrong:
|
|
248
|
+
|
|
249
|
+
```tsx
|
|
250
|
+
const [pagination] = React.useState<PaginationState>({
|
|
251
|
+
pageIndex: 0,
|
|
252
|
+
pageSize: 10,
|
|
253
|
+
})
|
|
254
|
+
useTable({
|
|
255
|
+
_features,
|
|
256
|
+
_rowModels: {},
|
|
257
|
+
columns,
|
|
258
|
+
data,
|
|
259
|
+
state: { pagination },
|
|
260
|
+
// missing onPaginationChange — table.setPageIndex is a no-op
|
|
261
|
+
manualPagination: true,
|
|
262
|
+
})
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Correct:
|
|
266
|
+
|
|
267
|
+
```tsx
|
|
268
|
+
const [pagination, setPagination] = React.useState<PaginationState>({
|
|
269
|
+
pageIndex: 0,
|
|
270
|
+
pageSize: 10,
|
|
271
|
+
})
|
|
272
|
+
useTable({
|
|
273
|
+
_features,
|
|
274
|
+
_rowModels: {},
|
|
275
|
+
columns,
|
|
276
|
+
data,
|
|
277
|
+
state: { pagination },
|
|
278
|
+
onPaginationChange: setPagination, // ← required
|
|
279
|
+
manualPagination: true,
|
|
280
|
+
})
|
|
281
|
+
// OR — use atoms.pagination instead, which doesn't need a writeback handler.
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
The library treats `state` as controlled; without a writeback handler, `table.setPageIndex(...)` writes nowhere.
|
|
285
|
+
Source: `docs/framework/react/guide/table-state.md`.
|
|
286
|
+
|
|
287
|
+
### HIGH Leaving `paginatedRowModel` registered for a server-paginated table
|
|
288
|
+
|
|
289
|
+
Wrong:
|
|
290
|
+
|
|
291
|
+
```tsx
|
|
292
|
+
useTable({
|
|
293
|
+
_features,
|
|
294
|
+
_rowModels: { paginatedRowModel: createPaginatedRowModel() }, // ships for nothing
|
|
295
|
+
columns,
|
|
296
|
+
data: serverPage.rows,
|
|
297
|
+
manualPagination: true,
|
|
298
|
+
})
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
Correct:
|
|
302
|
+
|
|
303
|
+
```tsx
|
|
304
|
+
useTable({
|
|
305
|
+
_features,
|
|
306
|
+
_rowModels: {}, // drop it — server owns pagination
|
|
307
|
+
columns,
|
|
308
|
+
data: serverPage.rows,
|
|
309
|
+
rowCount: serverPage.rowCount,
|
|
310
|
+
manualPagination: true,
|
|
311
|
+
})
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
The factory ships in your bundle for no reason. Manual mode + the factory will also let the factory re-slice your already-sliced server page if `manualPagination` is ever flipped off.
|
|
315
|
+
Source: maintainer guidance.
|
|
316
|
+
|
|
317
|
+
### HIGH Mixing `state.X` and `atoms.X` for the same slice
|
|
318
|
+
|
|
319
|
+
Wrong:
|
|
320
|
+
|
|
321
|
+
```tsx
|
|
322
|
+
useTable({
|
|
323
|
+
_features,
|
|
324
|
+
_rowModels: {},
|
|
325
|
+
columns,
|
|
326
|
+
data,
|
|
327
|
+
state: { pagination }, // silently ignored
|
|
328
|
+
onPaginationChange: setPagination, // silently ignored
|
|
329
|
+
atoms: { pagination: paginationAtom }, // wins
|
|
330
|
+
manualPagination: true,
|
|
331
|
+
})
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
Correct:
|
|
335
|
+
|
|
336
|
+
```tsx
|
|
337
|
+
// Pick one ownership mechanism per slice.
|
|
338
|
+
useTable({
|
|
339
|
+
_features,
|
|
340
|
+
_rowModels: {},
|
|
341
|
+
columns,
|
|
342
|
+
data,
|
|
343
|
+
atoms: { pagination: paginationAtom },
|
|
344
|
+
manualPagination: true,
|
|
345
|
+
})
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
Precedence is `options.atoms[key]` > `options.state[key]` > internal. The `state` plumbing is dead code in this configuration.
|
|
349
|
+
Source: `examples/react/basic-external-atoms/src/main.tsx`.
|
|
350
|
+
|
|
351
|
+
### MEDIUM Recreating `data` array identity in JSX
|
|
352
|
+
|
|
353
|
+
Wrong:
|
|
354
|
+
|
|
355
|
+
```tsx
|
|
356
|
+
<MyTable data={query.data?.rows ?? []} columns={columns} />
|
|
357
|
+
// `?? []` produces a new array reference each render → internal memos bust
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
Correct:
|
|
361
|
+
|
|
362
|
+
```tsx
|
|
363
|
+
const EMPTY: Person[] = [] // module scope
|
|
364
|
+
|
|
365
|
+
<MyTable data={query.data?.rows ?? EMPTY} columns={columns} />
|
|
366
|
+
// or wrap in useMemo
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
Internal memoization keys off identity. A fresh `[]` each render bypasses memos and may force re-computation.
|
|
370
|
+
Source: maintainer guidance; `examples/react/with-tanstack-query/src/main.tsx`.
|
|
371
|
+
|
|
372
|
+
## See Also
|
|
373
|
+
|
|
374
|
+
- `tanstack-table/react/compose-with-tanstack-query` — the canonical Query + server-side pattern.
|
|
375
|
+
- `tanstack-table/react/compose-with-tanstack-store` — sharing slice atoms across components.
|
|
376
|
+
- `tanstack-table/react/table-state` — selectors, `<Subscribe>`, external atoms.
|
|
377
|
+
- `tanstack-table/react/production-readiness` — when to narrow selectors as the table grows.
|