useParameterPersistence
Persist a serializable view-parameter snapshot (search, filters, sort, column layout, …) per saved view and restore it on the next mount, backed by a synchronous native key-value store.
Installation
pnpm dlx @docyrus/cli add @docyrus/rn-hooks-use-parameter-persistenceThe module exports the useParameterPersistence hook and the helpers the Docyrus listing hooks share: resolvePersistConfig, buildPersistKey, getPersistStore, plus the PersistStateProp types. It is the engine behind the persistState option of the Docyrus listing hooks.
Overview
The hook takes a snapshot object, restores a stored copy once per storage key, and writes changes back after a debounce.
- Restore once per key. When
storageKeychanges (for example the user switches to another saved view), the stored overlay for the new key is restored. - No write before restore. The first snapshot for a key never overwrites stored state before that key has been restored.
- Ordering. Declare the hook after any effect that applies a saved view, so the restored overlay lands on top of the view preset in the same commit.
- Failure tolerant. Corrupt JSON, a failing store or a serialization error is ignored.
Storage on React Native
React Native has no localStorage / sessionStorage, so storage resolves through the package's lib/storage module:
| Mode | Backend |
|---|---|
'session' (default) | In-memory Map that lives as long as the JS runtime (until the app is killed or reloaded). |
'local' | The store you register with <DocyStorageProvider> or setDocyLocalStore(). Without one: globalThis.localStorage if present (after import 'expo-sqlite/localStorage/install'), otherwise a separate in-memory Map. |
The contract is synchronous (getItem / setItem / removeItem), because the listing hooks read their state during mount. Recommended backends are expo-sqlite/kv-store (works in Expo Go) and react-native-mmkv (dev build). AsyncStorage is asynchronous and does not fit.
Mount <DocyStorageProvider> above every Docyrus listing hook. It registers the store synchronously during render, because child mount effects run before parent effects.
import Storage from 'expo-sqlite/kv-store';
import { DocyStorageProvider, type DocyKeyValueStore } from '@/lib/docyrus/storage';
const kvStore: DocyKeyValueStore = {
getItem: key => Storage.getItemSync(key),
setItem: (key, value) => Storage.setItemSync(key, value),
removeItem: (key) => {
Storage.removeItemSync(key);
}
};
export function AppProviders({ children }: { children: React.ReactNode }) {
return <DocyStorageProvider store={kvStore}>{children}</DocyStorageProvider>;
}lib/storage also exports setDocyLocalStore(store | null) (register outside React), getDocyStore(kind), getInjectedDocyLocalStore() and createMemoryStore().
Usage
import { useCallback, useMemo, useState } from 'react';
import {
buildPersistKey,
resolvePersistConfig,
useParameterPersistence,
type PersistStateProp
} from '@/hooks/docyrus-native/use-parameter-persistence';
type ListParams = { search: string; sort: string };
export function useContactListParams(viewId: string | null, persistState?: PersistStateProp) {
const [search, setSearch] = useState('');
const [sort, setSort] = useState('name');
const config = resolvePersistConfig(persistState);
const storageKey = config && viewId
? buildPersistKey(['crm', config.key || 'contacts', viewId])
: null;
const snapshot = useMemo<ListParams>(() => ({ search, sort }), [search, sort]);
const onRestore = useCallback((restored: Partial<ListParams>) => {
if (restored.search !== undefined) setSearch(restored.search);
if (restored.sort !== undefined) setSort(restored.sort);
}, []);
useParameterPersistence<ListParams>({
enabled: Boolean(config),
storage: config?.storage ?? 'session',
storageKey,
ready: viewId !== null,
snapshot,
onRestore
});
return { search, setSearch, sort, setSort };
}API Reference
useParameterPersistence(args)
Returns void.
| Argument | Type | Default | Description |
|---|---|---|---|
enabled | boolean | — | Whether persistence is active at all. |
storage | ParameterPersistenceStorage | — | 'session' or 'local'. |
storageKey | string | null | — | Full storage key including the active view id. null suspends restore and write until it resolves. |
ready | boolean | — | Defers restore and write until the surrounding view layer has settled (fields loaded, saved view applied). |
snapshot | T extends object | — | Current serializable parameter snapshot. Memoize it; it is serialized with JSON.stringify on change. |
onRestore | (restored: Partial<T>) => void | — | Applies a stored snapshot back into state. Read through a ref, so an inline function is fine. |
debounceMs | number | 300 | Debounce for writes. |
resolvePersistConfig(prop)
| Parameter | Type | Description |
|---|---|---|
prop | PersistStateProp | undefined | The public persistState option. |
Returns: { storage: ParameterPersistenceStorage; key: string } | null. true becomes { storage: 'session', key: '' }; an object fills storage (default 'session') and key (default ''); falsy returns null.
buildPersistKey(parts)
| Parameter | Type | Description |
|---|---|---|
parts | ReadonlyArray<string | undefined | null> | Key segments. Falsy parts are dropped. |
Returns: string, e.g. docyrus:view-params:<appSlug>:<dataSourceSlug>:<viewId>.
getPersistStore(storage)
| Parameter | Type | Description |
|---|---|---|
storage | ParameterPersistenceStorage | 'session' or 'local'. |
Returns: DocyKeyValueStore | null. On native a store is always resolved (see the storage table); null only if resolving throws. Shared with the saved-view hooks so every layer resolves storage the same way.
Type Exports
| Type | Description |
|---|---|
ParameterPersistenceStorage | 'session' | 'local'. |
ParameterPersistenceConfig | { storage?: ParameterPersistenceStorage; key?: string }. key overrides the data-source segment so two listings of the same data source don't share state. |
PersistStateProp | boolean | ParameterPersistenceConfig. true is shorthand for { storage: 'session' }. |
useNumberFormat
Provider-agnostic number formatting context for Docyrus native components. Mirror of useDateFormat, wired automatically by DocyrusTenantProvider.
useSpeechRecognition
Live speech-to-text for React Native with the same API as the web hook, backed by the optional expo-speech-recognition module.