58 lines
2.0 KiB
Plaintext
58 lines
2.0 KiB
Plaintext
---
|
|
title: Lifecycle and persistence
|
|
description: Coordinate SSR, asynchronous persistence, browser synchronization, and external input validation.
|
|
order: 20
|
|
toc:
|
|
- id: server-snapshots
|
|
title: Server snapshots
|
|
- id: persist-updates
|
|
title: Persist updates
|
|
- id: preference-effects
|
|
title: Preference effects
|
|
- id: validate-updates
|
|
title: Validate updates
|
|
---
|
|
|
|
## Server snapshots {#server-snapshots}
|
|
|
|
`initialPreferences` is captured when the provider creates its store. It remains the server and hydration snapshot; changing that prop later does not reset local preferences. Remount the provider when the application intentionally switches to a different preference identity.
|
|
|
|
## Persist updates {#persist-updates}
|
|
|
|
`onPreferenceChange` runs only when a value actually changes. It may return a promise, but local updates are not blocked while persistence completes. Handle retries, errors, and rollback behavior in the application boundary.
|
|
|
|
## Preference effects {#preference-effects}
|
|
|
|
Effects connect preferences to APIs outside React:
|
|
|
|
```tsx
|
|
const documentEffects = [
|
|
{
|
|
layoutEffect: ({ preferences }) => {
|
|
document.documentElement.dataset.theme = preferences["theme-mode"]
|
|
},
|
|
effect: ({ store }) => {
|
|
const media = window.matchMedia("(prefers-color-scheme: dark)")
|
|
const listener = () => synchronizeTheme(store.getSnapshot(), media)
|
|
|
|
media.addEventListener("change", listener)
|
|
return () => media.removeEventListener("change", listener)
|
|
},
|
|
},
|
|
] satisfies readonly PreferenceEffect[]
|
|
```
|
|
|
|
Use `layoutEffect` for DOM changes that must happen before paint. Use `effect` for subscriptions. Keep the effects array reference stable because callbacks and cleanup functions run again whenever the snapshot or array changes.
|
|
|
|
## Validate updates {#validate-updates}
|
|
|
|
Create a type guard from the same definitions before accepting data from an untyped boundary:
|
|
|
|
```ts
|
|
const isPreferenceUpdate = createPreferenceUpdateGuard(definitions)
|
|
|
|
if (isPreferenceUpdate(payload)) {
|
|
await savePreference(payload)
|
|
}
|
|
```
|