@datakable/react 0.0.0-stage → 1.0.0
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/LICENSE +21 -0
- package/README.md +127 -2
- package/dist/codes.cjs +66 -0
- package/dist/codes.cjs.map +1 -0
- package/dist/codes.js +42 -0
- package/dist/codes.js.map +1 -0
- package/dist/data-table.cjs +428 -0
- package/dist/data-table.cjs.map +1 -0
- package/dist/data-table.js +423 -0
- package/dist/data-table.js.map +1 -0
- package/dist/index.cjs +50 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +242 -0
- package/dist/index.d.ts +242 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/render-codes.cjs +78 -0
- package/dist/render-codes.cjs.map +1 -0
- package/dist/render-codes.js +54 -0
- package/dist/render-codes.js.map +1 -0
- package/dist/styles.css +1 -0
- package/dist/use-data-table.cjs +383 -0
- package/dist/use-data-table.cjs.map +1 -0
- package/dist/use-data-table.js +376 -0
- package/dist/use-data-table.js.map +1 -0
- package/package.json +57 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Supakit Kitjanabumrungsak
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,128 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @datakable/react
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
React bindings for DataKable: the `useDataTable` hook over the
|
|
4
|
+
[`@datakable/core`](https://github.com/Ink01101011/datakable/tree/main/packages/core) table store. Most apps want
|
|
5
|
+
[`datakable`](https://github.com/Ink01101011/datakable/tree/main/packages/datakable), whose `useDataTable` also installs the default feature
|
|
6
|
+
preset; this package installs **no** features unless you pass them.
|
|
7
|
+
|
|
8
|
+
> Version 1.0: the public API is stable and follows semantic versioning from here. Guides,
|
|
9
|
+
> examples and troubleshooting: the [documentation](https://github.com/Ink01101011/datakable/blob/main/docs/README.md).
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install @datakable/react @datakable/core react
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
React `>=18`. Every module of the bundle carries a `"use client"` directive, so the Next.js app
|
|
16
|
+
router treats `<DataTable />` and `useDataTable` imported from here as client code. Like core,
|
|
17
|
+
it needs a bundler that replaces `process.env.NODE_ENV` (see [core](https://github.com/Ink01101011/datakable/tree/main/packages/core)).
|
|
18
|
+
|
|
19
|
+
## useDataTable
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
import { type ColumnDef, pagination, sorting } from '@datakable/core'
|
|
23
|
+
import { useDataTable } from '@datakable/react'
|
|
24
|
+
import type { ReactNode } from 'react'
|
|
25
|
+
|
|
26
|
+
type User = { id: number; name: string; age: number }
|
|
27
|
+
|
|
28
|
+
// Columns are compared by reference: define them outside the component, or memoise them
|
|
29
|
+
// (DK_UNSTABLE_COLUMNS warns in development when a new same-shape array arrives every render).
|
|
30
|
+
const columns: ColumnDef<User, ReactNode>[] = [
|
|
31
|
+
{ key: 'name', header: 'Name', cell: ({ value }) => <strong>{value}</strong> },
|
|
32
|
+
{ key: 'age', header: 'Age' },
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
export function Users({ users }: { users: User[] }) {
|
|
36
|
+
// @datakable/react installs no features by default; `datakable` adds the preset.
|
|
37
|
+
const table = useDataTable({
|
|
38
|
+
data: users,
|
|
39
|
+
rowKey: 'id',
|
|
40
|
+
columns,
|
|
41
|
+
features: [sorting(), pagination({ pageSize: 20 })],
|
|
42
|
+
})
|
|
43
|
+
return (
|
|
44
|
+
<ul aria-busy={table.rowModel.status !== 'idle'}>
|
|
45
|
+
{table.rowModel.rows.map((row) => (
|
|
46
|
+
<li key={row.key}>{row.original.name}</li>
|
|
47
|
+
))}
|
|
48
|
+
</ul>
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
`useDataTable(options)` takes `createTable`'s options (with `cell` / `invalidCell` returning a
|
|
54
|
+
`ReactNode`) and returns the table — its methods plus the installed features' APIs — with
|
|
55
|
+
`state` and `rowModel` for this render. The returned object is new whenever the table notifies
|
|
56
|
+
and the same object otherwise, so it works as a `useMemo` dependency.
|
|
57
|
+
|
|
58
|
+
- The table is created once per component instance (on the first render) and destroyed on
|
|
59
|
+
unmount. Renders read it through `useSyncExternalStore`.
|
|
60
|
+
- **Read once, at creation:** `features`, `schema`, `invalidRows`, `initialState`,
|
|
61
|
+
`initialData`, `name`, `timeout`, `strict`, `diagnostics`. A new inline `features` array never
|
|
62
|
+
recreates the table. Hoist a `features` array with `as const`: as a plain array type the
|
|
63
|
+
table's type loses the features' methods and state.
|
|
64
|
+
- **Synced** through `table.setOptions` in a layout effect, never during render: `data`,
|
|
65
|
+
`columns`, `rowKey`, `state`, `onStateChange`, `interceptors`, `onDiagnostic`, `fetcher`,
|
|
66
|
+
`rowCount`.
|
|
67
|
+
- `onStateChange` and `onDiagnostic` may be inline functions. `fetcher`, `onStateChange` and
|
|
68
|
+
`onDiagnostic` reach the table as stable wrappers that call the latest prop, so a new reference
|
|
69
|
+
does not refetch — call `refetch()` when a value the fetcher reads changes. Wrap the fetcher
|
|
70
|
+
with `withFetcher()` (`@datakable/core/server`, also exported by `datakable`), and keep the
|
|
71
|
+
function you wrap stable: `withFetcher` returns the same wrapper for the same function, so
|
|
72
|
+
`fetcher={withFetcher(fetchUsers)}` with a module-level `fetchUsers` is fine, but an inline
|
|
73
|
+
arrow function is a new fetcher on every render and reports `DK_UNSTABLE_OPTION` in development
|
|
74
|
+
(hoist it or `useCallback` it).
|
|
75
|
+
- `columns`, `data` and a `rowKey` function are compared by reference. In development,
|
|
76
|
+
`DK_UNSTABLE_COLUMNS` / `DK_UNSTABLE_OPTION` warn when a new same-shape value arrives on every
|
|
77
|
+
render. Structural comparison is not used, because a changed `format` or `accessor` would then
|
|
78
|
+
go stale silently.
|
|
79
|
+
- Controlled slices: pass `state` and `onStateChange` (example in
|
|
80
|
+
[`datakable`](https://github.com/Ink01101011/datakable/tree/main/packages/datakable#controlled-state)).
|
|
81
|
+
- Status-driven UI: render from `table.rowModel.status` and `isInitialLoad` (the
|
|
82
|
+
[status model](https://github.com/Ink01101011/datakable/tree/main/packages/core#status-model)).
|
|
83
|
+
|
|
84
|
+
## DataTable
|
|
85
|
+
|
|
86
|
+
```tsx
|
|
87
|
+
import { type ColumnDef, pagination, search, sorting } from '@datakable/core'
|
|
88
|
+
import { DataTable } from '@datakable/react'
|
|
89
|
+
import type { ReactNode } from 'react'
|
|
90
|
+
|
|
91
|
+
type User = { id: number; name: string; age: number }
|
|
92
|
+
|
|
93
|
+
const columns: ColumnDef<User, ReactNode>[] = [
|
|
94
|
+
{ key: 'name', header: 'Name' },
|
|
95
|
+
{ key: 'age', header: 'Age', sortable: false },
|
|
96
|
+
]
|
|
97
|
+
// @datakable/react's DataTable renders what the installed features provide (none by default).
|
|
98
|
+
const features = [search(), sorting(), pagination({ pageSize: 20 })]
|
|
99
|
+
|
|
100
|
+
export function Users({ users }: { users: User[] }) {
|
|
101
|
+
return <DataTable data={users} rowKey="id" columns={columns} features={features} />
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
`<DataTable />` renders sort headers with `aria-sort`, a search input (Enter = `flushSearch`, Esc =
|
|
106
|
+
`clearSearch`), pagination, and Loading / Error / Empty states from `status` + `isInitialLoad`,
|
|
107
|
+
for the features installed (this package installs none by default). Pass `table={useDataTable(…)}`
|
|
108
|
+
to render a table you set up yourself, `components` to replace the search input, pager or states,
|
|
109
|
+
and style it through the `data-*` hooks (`data-status`, `data-initial-load`, `data-sort`,
|
|
110
|
+
`data-invalid`), or import the optional `@datakable/react/styles.css` (zero-specificity rules,
|
|
111
|
+
themed through `--dk-*` custom properties). A `cell` that throws renders `⚠` in that cell
|
|
112
|
+
(`DK_CALLBACK_FAILED`); so does a component it returns that throws while rendering — each such cell
|
|
113
|
+
has its own error boundary, and the other cells keep rendering (`DK_RENDER_FAILED`). A component
|
|
114
|
+
that throws elsewhere in the body shows the Error state with a retry.
|
|
115
|
+
|
|
116
|
+
## SSR
|
|
117
|
+
|
|
118
|
+
`useDataTable` and `<DataTable />` render on the server: `initialState` applies there (client-mode
|
|
119
|
+
features filter, search and sort the HTML), and no async work starts. The fetcher's first request
|
|
120
|
+
and a `searchFn` for an initial query run after hydration (`status: 'pending'` until then, shown
|
|
121
|
+
as Loading by `<DataTable />`), unless `initialData` holds their result, in which case hydration
|
|
122
|
+
does not fetch again. A table that changed before the hydration commit reports
|
|
123
|
+
`DK_HYDRATION_MISMATCH`. Example:
|
|
124
|
+
[`datakable` SSR](https://github.com/Ink01101011/datakable/tree/main/packages/datakable#ssr).
|
|
125
|
+
|
|
126
|
+
## License
|
|
127
|
+
|
|
128
|
+
MIT
|
package/dist/codes.cjs
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use strict";
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
|
|
21
|
+
// src/codes.ts
|
|
22
|
+
var codes_exports = {};
|
|
23
|
+
__export(codes_exports, {
|
|
24
|
+
reactDiagnostics: () => reactDiagnostics
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(codes_exports);
|
|
27
|
+
var import_core = require("@datakable/core");
|
|
28
|
+
var isFeatureOption = (option) => option.startsWith("features[");
|
|
29
|
+
var reactDiagnostics = process.env.NODE_ENV !== "production" ? [
|
|
30
|
+
(0, import_core.defineDiagnostic)({
|
|
31
|
+
code: "DK_UNSTABLE_COLUMNS",
|
|
32
|
+
level: "warn",
|
|
33
|
+
where: () => ({ kind: "option", path: "columns" }),
|
|
34
|
+
problem: (d) => `useDataTable received a new columns array with the same shape on ${d.renders} renders in a row (columns are compared by reference)`,
|
|
35
|
+
impact: () => "each new array rebuilds the column cache and re-runs the pipeline; in a re-render caused by the table itself, the columns the table already has are kept",
|
|
36
|
+
fix: () => [
|
|
37
|
+
"define the columns outside the component, or memoise them: useMemo(() => [...columns], [the values they read])",
|
|
38
|
+
"a new columns array is how you tell the table the columns changed (accessor / format / cell), so create one only when they do"
|
|
39
|
+
]
|
|
40
|
+
}),
|
|
41
|
+
(0, import_core.defineDiagnostic)({
|
|
42
|
+
code: "DK_UNSTABLE_OPTION",
|
|
43
|
+
level: "warn",
|
|
44
|
+
where: (d) => ({ kind: "option", path: d.option }),
|
|
45
|
+
problem: (d) => `useDataTable received a new ${d.option} reference on ${d.renders} renders in a row`,
|
|
46
|
+
impact: (d) => d.option === "fetcher" ? "the table always calls the latest fetcher, but a new fetcher does not refetch: values it reads that are not table state do not reload the data" : d.option === "data" ? "each new array is ingested again (schema, data checks, pipeline); in a re-render caused by the table itself an array with equal rows is skipped" : isFeatureOption(d.option) ? `features are read once, when the table is created: the table keeps calling the ${d.option} of the first render (a stale closure)` : "each new rowKey function recomputes every row key",
|
|
47
|
+
fix: (d) => d.option === "fetcher" ? [
|
|
48
|
+
"call table.refetch() when a value the fetcher reads changes (or keep that value in a server-mode feature state, which refetches by itself)",
|
|
49
|
+
"wrap the fetcher in useCallback to silence this warning"
|
|
50
|
+
] : d.option === "data" ? [
|
|
51
|
+
"memoise derived data: useMemo(() => rows.filter(...), [rows])",
|
|
52
|
+
"or keep the data in state and pass the same array until it changes"
|
|
53
|
+
] : isFeatureOption(d.option) ? [
|
|
54
|
+
"create the features once: useMemo(() => [search({ searchFn })], []) or outside the component",
|
|
55
|
+
"read changing values inside the function through a ref (ref.current) instead of the render closure"
|
|
56
|
+
] : [
|
|
57
|
+
"use a key path (rowKey: 'id') or a function defined outside the component",
|
|
58
|
+
"or wrap it in useCallback"
|
|
59
|
+
]
|
|
60
|
+
})
|
|
61
|
+
] : [];
|
|
62
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
63
|
+
0 && (module.exports = {
|
|
64
|
+
reactDiagnostics
|
|
65
|
+
});
|
|
66
|
+
//# sourceMappingURL=codes.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/codes.ts"],"sourcesContent":["import { defineDiagnostic } from '@datakable/core'\n\ndeclare module '@datakable/core' {\n interface DiagnosticCatalog {\n DK_UNSTABLE_COLUMNS: {\n /** Consecutive renders that passed a new `columns` array with the same shape. */\n renders: number\n }\n DK_UNSTABLE_OPTION: {\n /** `data`, `fetcher`, `rowKey`, or a feature's function option: `features[2].searchFn`. */\n option: string\n /** Consecutive renders that passed a new reference. */\n renders: number\n }\n }\n}\n\n// No @types/node here: declare just what is read.\ndeclare const process: { env: { NODE_ENV?: string } }\n\nconst isFeatureOption = (option: string) => option.startsWith('features[')\n\n/**\n * Codes owned by `@datakable/react` (§8.3, react, dev only). Like core's dev checks, the\n * definitions exist only in development: the NODE_ENV check is written out here (not an imported\n * flag) so a consumer's bundler folds it while parsing and drops the text from production bundles\n * (§8.2). They are never emitted in production, so `datakable/messages` has no text for them.\n */\nexport const reactDiagnostics =\n process.env.NODE_ENV !== 'production'\n ? [\n defineDiagnostic({\n code: 'DK_UNSTABLE_COLUMNS',\n level: 'warn',\n where: () => ({ kind: 'option', path: 'columns' }),\n problem: (d) =>\n `useDataTable received a new columns array with the same shape on ${d.renders} renders ` +\n 'in a row (columns are compared by reference)',\n impact: () =>\n 'each new array rebuilds the column cache and re-runs the pipeline; in a re-render caused ' +\n 'by the table itself, the columns the table already has are kept',\n fix: () => [\n 'define the columns outside the component, or memoise them: ' +\n 'useMemo(() => [...columns], [the values they read])',\n 'a new columns array is how you tell the table the columns changed (accessor / format / ' +\n 'cell), so create one only when they do',\n ],\n }),\n defineDiagnostic({\n code: 'DK_UNSTABLE_OPTION',\n level: 'warn',\n where: (d) => ({ kind: 'option', path: d.option }),\n problem: (d) =>\n `useDataTable received a new ${d.option} reference on ${d.renders} renders in a row`,\n impact: (d) =>\n d.option === 'fetcher'\n ? 'the table always calls the latest fetcher, but a new fetcher does not refetch: ' +\n 'values it reads that are not table state do not reload the data'\n : d.option === 'data'\n ? 'each new array is ingested again (schema, data checks, pipeline); in a re-render ' +\n 'caused by the table itself an array with equal rows is skipped'\n : isFeatureOption(d.option)\n ? 'features are read once, when the table is created: the table keeps calling the ' +\n `${d.option} of the first render (a stale closure)`\n : 'each new rowKey function recomputes every row key',\n fix: (d) =>\n d.option === 'fetcher'\n ? [\n 'call table.refetch() when a value the fetcher reads changes (or keep that value in ' +\n 'a server-mode feature state, which refetches by itself)',\n 'wrap the fetcher in useCallback to silence this warning',\n ]\n : d.option === 'data'\n ? [\n 'memoise derived data: useMemo(() => rows.filter(...), [rows])',\n 'or keep the data in state and pass the same array until it changes',\n ]\n : isFeatureOption(d.option)\n ? [\n 'create the features once: useMemo(() => [search({ searchFn })], []) or outside ' +\n 'the component',\n 'read changing values inside the function through a ref (ref.current) instead ' +\n 'of the render closure',\n ]\n : [\n \"use a key path (rowKey: 'id') or a function defined outside the component\",\n 'or wrap it in useCallback',\n ],\n }),\n ]\n : []\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kBAAiC;AAoBjC,IAAM,kBAAkB,CAAC,WAAmB,OAAO,WAAW,WAAW;AAQlE,IAAM,mBACX,QAAQ,IAAI,aAAa,eACrB;AAAA,MACE,8BAAiB;AAAA,IACf,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO,OAAO,EAAE,MAAM,UAAU,MAAM,UAAU;AAAA,IAChD,SAAS,CAAC,MACR,oEAAoE,EAAE,OAAO;AAAA,IAE/E,QAAQ,MACN;AAAA,IAEF,KAAK,MAAM;AAAA,MACT;AAAA,MAEA;AAAA,IAEF;AAAA,EACF,CAAC;AAAA,MACD,8BAAiB;AAAA,IACf,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO,CAAC,OAAO,EAAE,MAAM,UAAU,MAAM,EAAE,OAAO;AAAA,IAChD,SAAS,CAAC,MACR,+BAA+B,EAAE,MAAM,iBAAiB,EAAE,OAAO;AAAA,IACnE,QAAQ,CAAC,MACP,EAAE,WAAW,YACT,mJAEA,EAAE,WAAW,SACX,oJAEA,gBAAgB,EAAE,MAAM,IACtB,kFACG,EAAE,MAAM,2CACX;AAAA,IACV,KAAK,CAAC,MACJ,EAAE,WAAW,YACT;AAAA,MACE;AAAA,MAEA;AAAA,IACF,IACA,EAAE,WAAW,SACX;AAAA,MACE;AAAA,MACA;AAAA,IACF,IACA,gBAAgB,EAAE,MAAM,IACtB;AAAA,MACE;AAAA,MAEA;AAAA,IAEF,IACA;AAAA,MACE;AAAA,MACA;AAAA,IACF;AAAA,EACZ,CAAC;AACH,IACA,CAAC;","names":[]}
|
package/dist/codes.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/codes.ts
|
|
4
|
+
import { defineDiagnostic } from "@datakable/core";
|
|
5
|
+
var isFeatureOption = (option) => option.startsWith("features[");
|
|
6
|
+
var reactDiagnostics = process.env.NODE_ENV !== "production" ? [
|
|
7
|
+
defineDiagnostic({
|
|
8
|
+
code: "DK_UNSTABLE_COLUMNS",
|
|
9
|
+
level: "warn",
|
|
10
|
+
where: () => ({ kind: "option", path: "columns" }),
|
|
11
|
+
problem: (d) => `useDataTable received a new columns array with the same shape on ${d.renders} renders in a row (columns are compared by reference)`,
|
|
12
|
+
impact: () => "each new array rebuilds the column cache and re-runs the pipeline; in a re-render caused by the table itself, the columns the table already has are kept",
|
|
13
|
+
fix: () => [
|
|
14
|
+
"define the columns outside the component, or memoise them: useMemo(() => [...columns], [the values they read])",
|
|
15
|
+
"a new columns array is how you tell the table the columns changed (accessor / format / cell), so create one only when they do"
|
|
16
|
+
]
|
|
17
|
+
}),
|
|
18
|
+
defineDiagnostic({
|
|
19
|
+
code: "DK_UNSTABLE_OPTION",
|
|
20
|
+
level: "warn",
|
|
21
|
+
where: (d) => ({ kind: "option", path: d.option }),
|
|
22
|
+
problem: (d) => `useDataTable received a new ${d.option} reference on ${d.renders} renders in a row`,
|
|
23
|
+
impact: (d) => d.option === "fetcher" ? "the table always calls the latest fetcher, but a new fetcher does not refetch: values it reads that are not table state do not reload the data" : d.option === "data" ? "each new array is ingested again (schema, data checks, pipeline); in a re-render caused by the table itself an array with equal rows is skipped" : isFeatureOption(d.option) ? `features are read once, when the table is created: the table keeps calling the ${d.option} of the first render (a stale closure)` : "each new rowKey function recomputes every row key",
|
|
24
|
+
fix: (d) => d.option === "fetcher" ? [
|
|
25
|
+
"call table.refetch() when a value the fetcher reads changes (or keep that value in a server-mode feature state, which refetches by itself)",
|
|
26
|
+
"wrap the fetcher in useCallback to silence this warning"
|
|
27
|
+
] : d.option === "data" ? [
|
|
28
|
+
"memoise derived data: useMemo(() => rows.filter(...), [rows])",
|
|
29
|
+
"or keep the data in state and pass the same array until it changes"
|
|
30
|
+
] : isFeatureOption(d.option) ? [
|
|
31
|
+
"create the features once: useMemo(() => [search({ searchFn })], []) or outside the component",
|
|
32
|
+
"read changing values inside the function through a ref (ref.current) instead of the render closure"
|
|
33
|
+
] : [
|
|
34
|
+
"use a key path (rowKey: 'id') or a function defined outside the component",
|
|
35
|
+
"or wrap it in useCallback"
|
|
36
|
+
]
|
|
37
|
+
})
|
|
38
|
+
] : [];
|
|
39
|
+
export {
|
|
40
|
+
reactDiagnostics
|
|
41
|
+
};
|
|
42
|
+
//# sourceMappingURL=codes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/codes.ts"],"sourcesContent":["import { defineDiagnostic } from '@datakable/core'\n\ndeclare module '@datakable/core' {\n interface DiagnosticCatalog {\n DK_UNSTABLE_COLUMNS: {\n /** Consecutive renders that passed a new `columns` array with the same shape. */\n renders: number\n }\n DK_UNSTABLE_OPTION: {\n /** `data`, `fetcher`, `rowKey`, or a feature's function option: `features[2].searchFn`. */\n option: string\n /** Consecutive renders that passed a new reference. */\n renders: number\n }\n }\n}\n\n// No @types/node here: declare just what is read.\ndeclare const process: { env: { NODE_ENV?: string } }\n\nconst isFeatureOption = (option: string) => option.startsWith('features[')\n\n/**\n * Codes owned by `@datakable/react` (§8.3, react, dev only). Like core's dev checks, the\n * definitions exist only in development: the NODE_ENV check is written out here (not an imported\n * flag) so a consumer's bundler folds it while parsing and drops the text from production bundles\n * (§8.2). They are never emitted in production, so `datakable/messages` has no text for them.\n */\nexport const reactDiagnostics =\n process.env.NODE_ENV !== 'production'\n ? [\n defineDiagnostic({\n code: 'DK_UNSTABLE_COLUMNS',\n level: 'warn',\n where: () => ({ kind: 'option', path: 'columns' }),\n problem: (d) =>\n `useDataTable received a new columns array with the same shape on ${d.renders} renders ` +\n 'in a row (columns are compared by reference)',\n impact: () =>\n 'each new array rebuilds the column cache and re-runs the pipeline; in a re-render caused ' +\n 'by the table itself, the columns the table already has are kept',\n fix: () => [\n 'define the columns outside the component, or memoise them: ' +\n 'useMemo(() => [...columns], [the values they read])',\n 'a new columns array is how you tell the table the columns changed (accessor / format / ' +\n 'cell), so create one only when they do',\n ],\n }),\n defineDiagnostic({\n code: 'DK_UNSTABLE_OPTION',\n level: 'warn',\n where: (d) => ({ kind: 'option', path: d.option }),\n problem: (d) =>\n `useDataTable received a new ${d.option} reference on ${d.renders} renders in a row`,\n impact: (d) =>\n d.option === 'fetcher'\n ? 'the table always calls the latest fetcher, but a new fetcher does not refetch: ' +\n 'values it reads that are not table state do not reload the data'\n : d.option === 'data'\n ? 'each new array is ingested again (schema, data checks, pipeline); in a re-render ' +\n 'caused by the table itself an array with equal rows is skipped'\n : isFeatureOption(d.option)\n ? 'features are read once, when the table is created: the table keeps calling the ' +\n `${d.option} of the first render (a stale closure)`\n : 'each new rowKey function recomputes every row key',\n fix: (d) =>\n d.option === 'fetcher'\n ? [\n 'call table.refetch() when a value the fetcher reads changes (or keep that value in ' +\n 'a server-mode feature state, which refetches by itself)',\n 'wrap the fetcher in useCallback to silence this warning',\n ]\n : d.option === 'data'\n ? [\n 'memoise derived data: useMemo(() => rows.filter(...), [rows])',\n 'or keep the data in state and pass the same array until it changes',\n ]\n : isFeatureOption(d.option)\n ? [\n 'create the features once: useMemo(() => [search({ searchFn })], []) or outside ' +\n 'the component',\n 'read changing values inside the function through a ref (ref.current) instead ' +\n 'of the render closure',\n ]\n : [\n \"use a key path (rowKey: 'id') or a function defined outside the component\",\n 'or wrap it in useCallback',\n ],\n }),\n ]\n : []\n"],"mappings":";;;AAAA,SAAS,wBAAwB;AAoBjC,IAAM,kBAAkB,CAAC,WAAmB,OAAO,WAAW,WAAW;AAQlE,IAAM,mBACX,QAAQ,IAAI,aAAa,eACrB;AAAA,EACE,iBAAiB;AAAA,IACf,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO,OAAO,EAAE,MAAM,UAAU,MAAM,UAAU;AAAA,IAChD,SAAS,CAAC,MACR,oEAAoE,EAAE,OAAO;AAAA,IAE/E,QAAQ,MACN;AAAA,IAEF,KAAK,MAAM;AAAA,MACT;AAAA,MAEA;AAAA,IAEF;AAAA,EACF,CAAC;AAAA,EACD,iBAAiB;AAAA,IACf,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO,CAAC,OAAO,EAAE,MAAM,UAAU,MAAM,EAAE,OAAO;AAAA,IAChD,SAAS,CAAC,MACR,+BAA+B,EAAE,MAAM,iBAAiB,EAAE,OAAO;AAAA,IACnE,QAAQ,CAAC,MACP,EAAE,WAAW,YACT,mJAEA,EAAE,WAAW,SACX,oJAEA,gBAAgB,EAAE,MAAM,IACtB,kFACG,EAAE,MAAM,2CACX;AAAA,IACV,KAAK,CAAC,MACJ,EAAE,WAAW,YACT;AAAA,MACE;AAAA,MAEA;AAAA,IACF,IACA,EAAE,WAAW,SACX;AAAA,MACE;AAAA,MACA;AAAA,IACF,IACA,gBAAgB,EAAE,MAAM,IACtB;AAAA,MACE;AAAA,MAEA;AAAA,IAEF,IACA;AAAA,MACE;AAAA,MACA;AAAA,IACF;AAAA,EACZ,CAAC;AACH,IACA,CAAC;","names":[]}
|