Invariants
This page is the shortest authoritative statement of what react-mnemonic
guarantees.
Core Runtime Invariants
useMnemonicKey(...)must run inside aMnemonicProvider.- Every persisted key is stored as
${namespace}.${key}in the underlying backend. defaultValueis required and defines the fallback when a key is absent, malformed, or rejected by validation.set(next)persists the next value for the key.reset()persistsdefaultValueagain.remove()deletes the key entirely, so the next read falls back todefaultValue.listenCrossTabis advisory. Native browser storage uses thestorageevent; custom adapters must implementstorage.onExternalChange(...)for equivalent behavior.StorageLikeis synchronous. Returning a Promise fromgetItem,setItem, orremoveItemis an invalid contract.- SSR defaults to
defaultValueon the server unlessssr.serverValueis supplied. reconcile(...)runs after decode and migration. If it returns a different persisted result, the library rewrites storage with that reconciled value.- Schema migrations handle structural version-to-version upgrades.
reconcile(...)handles conditional read-time policy updates inside an already valid shape.
Type Sourcing Rules
- Import values from
react-mnemonic, not internal package paths. - Import exported types from
react-mnemonicwithimport type. - Do not create local
react-mnemonic.d.tsfiles. - Do not write
declare module "react-mnemonic"in consumer code. - If a type seems missing, check
src/index.ts,package.json, and the API docs before inventing a replacement contract.
Exact Read Lifecycle
Normal client reads follow this order:
- Load the raw snapshot for the key from the provider cache and storage layer.
- If the raw value is absent, use
defaultValue. - Parse the versioned envelope.
- Decode the payload through the codec path or schema-managed JSON path.
- Validate against the stored schema version when a schema exists.
- Run schema migrations when the stored version is older than the latest registered version.
- Run
reconcile(...)if provided. - If migration, autoschema inference, or reconciliation changed the persisted representation, schedule a rewrite back to storage.
- If any read path is invalid, fall back to
defaultValueand pass the relevant error to a fallback factory when applicable.
SSR and hydration add one rule before the normal read path:
- On the server, the hook uses the SSR snapshot instead of reading browser storage.
- Without explicit SSR config, that snapshot renders
defaultValue. - With
ssr.serverValue, that snapshot renders the provided placeholder. - With
hydration: "client-only", persisted storage is not read until after mount.
Exact Write Lifecycle
Writes follow this order:
- Resolve the next value directly or by calling the updater with the current decoded value.
- Choose the write path:
- schema-managed latest version by default
- explicit pinned schema version when configured
- codec-only v0 envelope when no schema applies
- Run a write-time migration when a registry rule has
fromVersion === toVersion. - Validate the value against the target schema when schema-managed.
- Encode the versioned envelope.
- Write the raw string through the provider cache and storage layer.
- Notify subscribers for the key.
Storage Adapter Boundaries
Custom storage adapters are supported, but the contract is intentionally strict:
- The adapter must be synchronous.
- The adapter must expose
getItem,setItem, andremoveItem. key(index)andlengthare optional and are only needed for enumeration-style features.- Cross-tab or external-process sync is not automatic for custom adapters. Add
onExternalChange(...)when your backend can detect out-of-band writes. - If you wrap
localStorage, preserve the nativestorageevent semantics or implementonExternalChange(...)yourself.