# useLocalDataSource URL: /docs/native/hooks/use-local-data-source Create an in-memory Docyrus-compatible data source from a JSON array, with local CRUD and Docyrus-style query parameters. ## Installation ```bash pnpm dlx @docyrus/cli add @docyrus/rn-hooks-use-local-data-source ``` **Dependencies:** - [jsonata](https://www.npmjs.com/package/jsonata) - [@docyrus/app-utils](https://www.npmjs.com/package/@docyrus/app-utils) - [expo-crypto (optional)](https://docs.expo.dev/versions/latest/sdk/crypto/) `@docyrus/app-utils` and `@docyrus/api-client` are imported for types only (`DataSource`, `DataSourceField`, `RestApiClient`). `expo-crypto` is optional: when installed, ids for created rows come from its `randomUUID()`; otherwise the hook uses a global `crypto.randomUUID()` when available and finally a timestamp-plus-random id. ## Overview `useLocalDataSource` turns a JSON array of objects into a local `LocalDataSource` object that behaves like a lightweight Docyrus data source. It is useful for prototypes, offline demos, local previews, and components that should run without a backend. It supports: - Local CRUD: `list`, `get`, `create`, `update`, `updateMany`, `delete`, `deleteMany`. - Docyrus-compatible field metadata, JSON Schema metadata, or inferred fields from row values. - Local query payloads modeled after `GET /apps/:appId/data-sources/:dataSourceId/items`. - `filterKeyword`, nested `filters`, `orderBy`, `limit`, `offset`, `columns`, `distinctColumns`, `calculations`, and basic `pivot`. - Local formula columns using JSONata expressions. `childQueries` are accepted in the query type but intentionally skipped. ## Usage ```tsx import { useState } from 'react'; import { Button } from '@/components/docyrus-native/button'; import { useLocalDataSource } from '@/hooks/docyrus-native/use-local-data-source'; type Deal = { id: string; account: string; stage: string; amount: number; probability: number; }; // Keep metadata stable (module scope or useMemo) so the instance is not rebuilt. const fields = [ { slug: 'id', name: 'ID', type: 'field-identity', readOnly: true }, { slug: 'account', name: 'Account', type: 'field-text' }, { slug: 'stage', name: 'Stage', type: 'field-select' }, { slug: 'amount', name: 'Amount', type: 'field-money' }, { slug: 'probability', name: 'Probability', type: 'field-percent' }, { slug: 'weighted_amount', name: 'Weighted Amount', type: 'field-money', readOnly: true } ]; export function LocalDeals() { const [rows, setRows] = useState; } ``` ## Metadata ### Docyrus fields Pass an array of field-like objects when you already know the Docyrus field shape: ```ts const metadata = [ { slug: 'name', name: 'Name', type: 'field-text' }, { slug: 'amount', name: 'Amount', type: 'field-money' }, { slug: 'closed_on', name: 'Closed On', type: 'field-date' } ]; ``` ### JSON Schema Pass a JSON Schema object and field types are mapped automatically: ```ts const metadata = { type: 'object', properties: { name: { type: 'string', title: 'Name' }, email: { type: 'string', format: 'email' }, amount: { type: 'number' }, active: { type: 'boolean' }, status: { type: 'string', enum: ['Open', 'Closed'] } } }; ``` ### Inferred fields If `metadata` is omitted, fields are inferred from all row keys. Values are sampled to choose simple Docyrus field types such as `field-text`, `field-number`, `field-checkbox`, `field-date`, `field-dateTime`, `field-email`, `field-url`, and `field-multiSelect`. ## Query Support `list(params)` returns `{ data, meta: { total } }`. `meta.total` is always the number of rows after formulas, keyword search, filters, calculations/pivot, sorting, and before `limit`/`offset`. | Parameter | Description | |-----------|-------------| | `columns` | Comma-separated field slugs. Used for projection and grouping when `calculations` are present. | | `filterKeyword` | Case-insensitive search across serialized row values. | | `filters` | Nested `and`/`or` filter groups with common Docyrus/querybuilder operators. | | `formulas` | Map of virtual column aliases to JSONata expressions. | | `calculations` | Aggregations over the filtered row set. Supports `count`, `sum`, `avg`, `min`, `max`, `jsonb_agg`, `json_agg`, and `array_agg`. | | `pivot` | Basic cross-tab output using the first matrix definition. | | `orderBy` | String, object, or array sort definition. | | `limit` / `offset` | Local pagination. | | `distinctColumns` | Dedupe rows by one or more columns before projection. | ### Filters Supported operators include equality/inequality, comparisons, text matching, set membership, ranges, null/empty checks, and their negated variants: ```ts await source.list({ filters: { combinator: 'and', rules: [ { field: 'stage', operator: 'notIn', value: ['Closed Lost'] }, { field: 'amount', operator: '>=', value: 50000 }, { combinator: 'or', rules: [ { field: 'region', operator: '=', value: 'EMEA' }, { field: 'region', operator: '=', value: 'APAC' } ] } ] } }); ``` ### JSONata formulas Each formula runs against the current row and writes a virtual column with the formula key: ```ts await source.list({ formulas: { weighted_amount: { jsonata: 'amount * probability' }, display_name: { jsonata: 'account & " · " & stage' } }, columns: 'account, weighted_amount, display_name' }); ``` ## API Reference ### `useLocalDataSource(options)` | Option | Type | Default | Description | |--------|------|---------|-------------| | `data` | `Array` | — | Source rows. Required. Rows without `id` receive a generated id from `getRowId`. | | `metadata` | `LocalDataSourceMetadata` | inferred | Docyrus field list or JSON Schema. | | `id` | `string` | `'local-data-source'` | Data source id exposed through `dataSource.id`. | | `slug` | `string` | `'local'` | Data source slug. | | `appSlug` | `string` | `'local'` | App slug used by the local client shim. | | `name` | `string` | `'Local Data Source'` | Display name. | | `getRowId` | `(row, index) => string` | `index + 1` | Generates ids for rows that do not already have an `id`. | | `onDataChange` | `(data) => void` | — | Called after local create/update/delete operations. Use this to keep React state in sync. | ### Return Value | Property | Type | Description | |----------|------|-------------| | `dataSource` | `DataSource` | Docyrus-compatible data source metadata. | | `fields` | `Array` | Normalized field list. | | `getData` | `() => Array` | Return the current local rows. | | `setData` | `(data) => void` | Replace all rows and call `onDataChange`. | | `syncData` | `(data) => void` | Refresh the row snapshot in place without firing `onDataChange`. Same array reference is a no-op, so the hook calls it on every render. | | `list` | `(params?) => Promise<{ data, meta: { total } }>` | Query rows with local Docyrus-style query params. | | `get` | `(id) => Promise` | Read one row by `id`. | | `create` | `(record) => Promise` | Insert a row and generate an id when needed. | | `update` | `(id, patch) => Promise` | Patch one row. Throws if the id does not exist. | | `updateMany` | `({ records }) => Promise>` | Bulk patch rows by id. Matches the collection shape the Docyrus data-grid hooks use. | | `delete` | `(id) => Promise` | Delete one row. | | `deleteMany` | `({ recordIds }) => Promise` | Bulk delete rows by id. | | `client` | `RestApiClient` | `RestApiClient` shim that answers data-source metadata (`/v1/apps/data-sources`), view (empty list) and `/items` requests from the local rows. `post` / `patch` / `delete` are no-ops. On web it backs `useLocalDataSourceDataGrid` (native port pending). | ### Instance stability The returned object is **not** rebuilt when `data` changes; rows are refreshed on the same instance through `syncData`. That keeps `dataSource` and `fields` referentially stable, so consumers don't recompute columns on every edit. Exception: without `metadata`, field types are inferred from the rows, so the instance is rebuilt whenever `data` changes. Keep `metadata` (and `getRowId` / `onDataChange`) stable to benefit. ## Exported types | Type | Description | |------|-------------| | `LocalDataSource` | Return value (see above). | | `LocalDataSourceOptions` | Options accepted by the hook and `createLocalDataSource`. | | `LocalDataSourceMetadata` | Field-like array or `LocalJsonSchema`. | | `LocalJsonSchema` | `{ type?, properties?, required? }` JSON Schema subset. | | `LocalDataSourceQueryParams` | Query payload accepted by `list`. | | `LocalDataSourceListResponse` | `{ data, meta: { total } }`. | | `LocalQueryFilterGroup` / `LocalQueryFilterRule` | Filter group (`combinator`, `rules`, `not`) and rule (`field`, `operator`, `value`). | | `LocalCalculationRule` | `{ func, field, name?, isDistinct?, minValue?, maxValue?, numberType? }`. | | `LocalFormulaDefinition` | Formula with `jsonata`, `value` or `expression`. | | `LocalOrderBy` | `{ field, direction?: 'asc' \| 'desc' }`. | | `LocalPivotQuery` | `{ matrix?, hideEmptyRows?, orderBy?, limit? }`. | ## `createLocalDataSource` `createLocalDataSource(options)` exposes the same local data source object without using React. Use it in tests, examples, or non-React local query helpers.