# useDocyrusInventory URL: /docs/native/hooks/use-docyrus-inventory Shared app-utils inventory cache for Docyrus metadata (apps, data sources with fields, enums, views and forms, users, brands, preferences) plus a post-sign-in warm-up hook that drives a loading screen. ## Installation ```bash pnpm dlx @docyrus/cli add @docyrus/rn-hooks-use-docyrus-inventory ``` **Dependencies:** - [@docyrus/app-utils](https://www.npmjs.com/package/@docyrus/app-utils) - [@docyrus/api-client](https://www.npmjs.com/package/@docyrus/api-client) The module exports: - `getSharedDocyrusInventory(client)`: returns the process-wide shared `InventoryClient` for a `RestApiClient`, creating it on first use. Instances are memoized in a `WeakMap` keyed by client. `null` in gives `null` out. - `useDocyrusInventoryLoader(options)`: headless hook that runs `inventory.load()` once per client after sign-in and reports progress, so the app can show a loading screen before the home screen. ## Why this exists `createInventoryClient` from `@docyrus/app-utils` caches the metadata a Docyrus app needs at startup: apps, data sources (with embedded `views`, `forms`, `fields` and field `enums`), users, brands and tenant preferences. The data sources come from one `GET /v1/apps/data-sources?expand=views,forms,fields` request. The cache lives inside the returned client, so everything must share **one** instance. Then a single warm-up fills the cache, and every inventory-backed Docyrus hook reads from it instead of fetching again on mount. `getSharedDocyrusInventory` hands out that instance; the Docyrus hooks call it internally. A new client identity (for example after a tenant switch that rebuilds the client) gets a fresh inventory. If you reuse the same client across a tenant switch, call `inventory.refresh()`. ## `getSharedDocyrusInventory(client)` ```ts import { createDataSourceClient } from '@docyrus/app-utils'; import { getSharedDocyrusInventory } from '@/hooks/docyrus-native/use-docyrus-inventory'; const inventory = getSharedDocyrusInventory(apiClient); const dataSources = createDataSourceClient(apiClient, { inventory }); // Served from the shared cache (fields, enums, views and forms embedded) const contacts = await dataSources.getBySlug('crm', 'contacts'); ``` | Parameter | Type | Description | |-----------|------|-------------| | `client` | `RestApiClient \| null \| undefined` | The authenticated API client. | **Returns:** `InventoryClient` for a client, `null` when `client` is nullish. Overloaded so a non-null client returns a non-null `InventoryClient` type. ## `useDocyrusInventoryLoader(options)` Runs `inventory.load()` once as soon as `enabled` is true and a `client` exists. It is StrictMode-safe and re-runs automatically when the client identity changes. Failures do not throw: `status` becomes `'error'` and `reload()` retries. The app still works on error, because inventory-backed reads fall back to the API. ```tsx import { ActivityIndicator, Text, View } from 'react-native'; import { useDocyrusAuth, useDocyrusClient } from '@docyrus/signin/react-native'; import { Button } from '@/components/docyrus-native/button'; import { Progress } from '@/components/docyrus-native/progress'; import { useDocyrusInventoryLoader } from '@/hooks/docyrus-native/use-docyrus-inventory'; const APP_ID = process.env.EXPO_PUBLIC_DOCYRUS_APP_ID; // Keep array options stable (module scope or useMemo). const CONFIG_APP_IDS = APP_ID ? [APP_ID] : []; export function InventoryGate({ children }: { children: React.ReactNode }) { const { status } = useDocyrusAuth(); const client = useDocyrusClient(); const { status: loadStatus, progress, reload } = useDocyrusInventoryLoader({ client, enabled: status === 'authenticated', configAppIds: CONFIG_APP_IDS }); if (loadStatus === 'ready') return children; return ( ) : ( <> )} ); } ``` ### Options (`UseDocyrusInventoryLoaderOptions`) | Option | Type | Default | Description | |--------|------|---------|-------------| | `client` | `RestApiClient \| null \| undefined` | — | The authenticated API client. The loader waits until it is set. | | `enabled` | `boolean` | `true` | Gate the warm-up (e.g. only once authenticated). A non-null `client` is still required. | | `include` | `InventoryLoadStepId[]` | app-utils default: `['apps', 'data-sources', 'users', 'brands', 'preferences']` | Tenant-wide steps to warm. Drop `'users'` when the user may lack the `Users.Read.All` scope. | | `configAppIds` | `string[]` | app-utils default | App ids whose config and user-config to warm. app-utils defaults to the `VITE_APP_ID` env var, which does not exist under Metro, so pass ids explicitly on native. `[]` skips config steps. | | `force` | `boolean` | app-utils default: `true` | Invalidate caches before loading so the warm-up makes fresh network calls. | ### Result (`DocyrusInventoryLoaderState`) | Field | Type | Description | |-------|------|-------------| | `inventory` | `InventoryClient \| null` | The shared inventory for the current client. | | `status` | `'idle' \| 'loading' \| 'ready' \| 'error'` | Warm-up lifecycle. | | `progress` | `InventoryLoadProgress \| null` | Latest progress event. `progress.ratio` (0 to 1) drives a determinate bar; `progress.message` is an English label. | | `error` | `unknown` | The load error when `status === 'error'`. | | `result` | `InventoryLoadResult \| null` | The loaded payload once ready. | | `isLoading` | `boolean` | `true` while warming. | | `isReady` | `boolean` | `true` once warmed successfully. | | `reload` | `() => void` | Re-run the warm-up (Retry button, tenant switch). | ## Type Exports | Type | Description | |------|-------------| | `DocyrusInventoryLoadStatus` | `'idle' \| 'loading' \| 'ready' \| 'error'`. | | `UseDocyrusInventoryLoaderOptions` | Options of `useDocyrusInventoryLoader`. | | `DocyrusInventoryLoaderState` | Return value of `useDocyrusInventoryLoader`. |