React Native / Asynchronous and data ownership / Lesson 11
Versioned drafts and storage adapters
Define what local storage promises and keep draft encoding, write acceptance, and authoritative synchronization distinct.
On this page
Lesson goals
- Different records require different storage guarantees
- Read a versioned envelope at one boundary
- Use a failure-capable fake to expose acceptance policy
Different records require different storage guarantees
A disposable response cache, an unsent draft, a credential, and a confirmed transaction have different lifetimes and consequences of loss. Define ownership, sensitivity, schema version, retention, and recovery before choosing an adapter. React Native core does not provide the removed AsyncStorage API; a selected community or platform integration has its own installation, native compatibility, and guarantees. No storage package is installed here. Avoid treating a preferences string store as protected credential storage or a transactional database. A successful local write is also not proof that an authoritative server accepted a reservation.
Read a versioned envelope at one boundary
The first program accepts a small schema1 draft envelope and rejects unsupported versions or a malformed title. A version marker lets future code choose a migration or recovery path instead of interpreting changed fields under old assumptions. JSON encoding produces text only; it does not establish disk persistence, encryption, or restart recovery. For valuable drafts, silently deleting incompatible content may be unacceptable. Preserve the failure category and decide whether to migrate, export, quarantine, or ask the current owner to recover. A migration should preserve meaning and validate its output, not merely silence an exception.
function readDraft(source) {
const raw = JSON.parse(source);
if (!raw || typeof raw !== 'object' || Array.isArray(raw)
|| raw.schema !== 1 || typeof raw.title !== 'string') {
throw new Error('UNSUPPORTED_DRAFT');
}
return {title: raw.title};
}
const encoded = JSON.stringify({schema: 1, title: 'Maps'});
console.log(readDraft(encoded).title);
try { readDraft('{"schema":2,"title":"Maps"}'); }
catch (error) { console.log(error.message); }
Maps
UNSUPPORTED_DRAFT
Only local serialization is exercised. A supported schema does not itself mean the title passed a separate save-command validation rule.
Use a failure-capable fake to expose acceptance policy
The second program preserves the previous local value when its synthetic adapter rejects a replacement. It demonstrates one adapter contract and the callerโs decision not to announce acceptance after a rejected promise. Actual storage products differ in atomicity, batching, durability, and failure recovery; this fake cannot grant them its behavior. Inject the adapter into a repository so tests can simulate failure without touching a device store. Keep important draft text available until the selected storage boundary confirms the promised operation or a deliberate cancellation policy discards it.
class MemoryDraftStore {
constructor() { this.value = null; this.rejectWrites = false; }
async write(value) {
if (this.rejectWrites) throw new Error('WRITE_REJECTED');
this.value = value;
}
async read() { return this.value; }
}
async function demo() {
const store = new MemoryDraftStore();
await store.write('Maps');
store.rejectWrites = true;
try { await store.write('Rivers'); }
catch (error) { console.log(error.message); }
console.log(await store.read());
}
demo().catch(error => { console.error(error.message); process.exitCode = 1; });
WRITE_REJECTED
Maps
This in-memory fake opens no file or native store. It validates caller-visible failure behavior only for its declared local contract.
Separate cache freshness from availability and ownership
Cached data can remain available while stale. Store enough safe metadata to explain that condition, and choose whether refresh keeps prior content visible. A local timestamp does not prove server truth. Scope private caches and unsent drafts to the intended account so logout cannot publish old content into a new session. An offline write queue needs stable operation identity, conflict rules, and uncertain-completion reconciliation; enqueuing is not business confirmation. Device connectivity is only a hint about network availability and does not establish endpoint reachability or authorization. Define those source policies in the repository.
Verify recovery instead of only successful round trips
Test malformed JSON, old and unsupported schemas, a missing title, failed reads, rejected writes, and restart between intent recording and confirmation. Keep schema migration fixtures from supported earlier formats. An in-memory fake can prove policy transitions but not disk durability, protected storage, process termination, or native plugin registration. Execute those boundaries only in the authorized target host and report them separately. No real storage package, database, credential store, device, or synchronization service is operated in this course; its programs remain small local models.
Common errors and fixes
Encoded JSON is reported as a durable save
- Why it happens
- Serialization was confused with storage completion and recovery.
- How to fix it
- Name the actual write boundary and its documented guarantees.
- Prevent it next time
- Test encoding separately from restart persistence.
An app update silently deletes incompatible drafts
- Why it happens
- No versioned recovery policy existed.
- How to fix it
- Preserve data and choose migration or recovery deliberately.
- Prevent it next time
- Retain prior-format synthetic fixtures.
Old account data appears after logout
- Why it happens
- Cache keys and result publication lacked account scope.
- How to fix it
- Invalidate private state under session ownership rules.
- Prevent it next time
- Change accounts while a storage read is pending.
Check your understanding
1. What does MemoryDraftStore prove about native storage atomicity?
Show worked solution
Nothing about a native implementation. It specifies and tests a synthetic adapter whose rejection leaves the previous value unchanged. A selected storage product must be evaluated under its own documented contract and target evidence; the fake is a controlled substitute for caller-policy tests.
2. Should a queued offline reservation be shown as confirmed?
Show worked solution
No. The queue preserves intent until authoritative evaluation. Availability or permission can change before delivery, and a lost response may require reconciliation. Show pending status with clear cancellation limits and retain a stable operation identifier according to the service contract.
Key takeaways
- Different records require different storage guarantees
- Verify recovery instead of only successful round trips