The syncObservable() function adds synchronization and persistence to an existing observable. Use this when you already have an observable and want to add sync capabilities, or when you need more control over the observable creation.
Usage
import { observable } from '@legendapp/state'
import { syncObservable } from '@legendapp/state/sync'
const data$ = observable({ items: [] })
const syncState$ = syncObservable(data$, {
get: () => fetch('/api/items').then(r => r.json()),
set: ({ value }) => fetch('/api/items', {
method: 'POST',
body: JSON.stringify(value)
}),
persist: {
name: 'items',
plugin: ObservablePersistLocalStorage
}
})
Parameters
The observable to add sync capabilities to
Sync configuration options. Same as synced() options.
Return Value
Returns an Observable<ObservableSyncState> that tracks the sync status:
interface ObservableSyncState {
// Loading states
isLoaded: Observable<boolean>
isPersistLoaded: Observable<boolean>
isLoading: Observable<boolean>
// Sync states
isSyncEnabled: Observable<boolean>
isPersistEnabled: Observable<boolean>
// Active operations
isSetting: Observable<boolean>
numPendingSets: Observable<number>
numPendingLocalLoads: Observable<number>
numPendingRemoteLoads: Observable<number>
// Timing
lastSync: Observable<number | undefined>
// Error handling
error: Observable<Error | undefined>
// Control functions
sync: (options?: ObservableSyncStateOptions) => Promise<void>
getPendingChanges: () => PendingChanges | undefined
// Lifecycle
clearPersist: () => Promise<void> // DEPRECATED: use resetPersistence
resetPersistence: () => Promise<void>
}
ObservableSyncState Fields
true when all remote loads have completed
true when local persistence has loaded
true when actively loading from remote
Enable/disable remote syncing// Disable syncing temporarily
syncState$.isSyncEnabled.set(false)
Enable/disable local persistence
true when actively saving to remote
Number of pending save operations
Number of pending local persistence loads
Number of pending remote loads
lastSync
Observable<number | undefined>
Timestamp of the last successful sync
error
Observable<Error | undefined>
Most recent error from sync operations
sync
(options?: ObservableSyncStateOptions) => Promise<void>
Manually trigger a sync// Refresh from remote
await syncState$.sync()
// Force refresh ignoring lastSync
await syncState$.sync({ resetLastSync: true })
getPendingChanges
() => PendingChanges | undefined
Get pending changes that haven’t synced yetReturns an object with paths as keys and change info as values:{
"path/to/field": {
p: previousValue,
v: newValue,
t: pathTypes
}
}
Clear all persisted data for this observable (both data and metadata)await syncState$.resetPersistence()
Advanced Examples
Monitoring Sync State
const data$ = observable({ count: 0 })
const syncState$ = syncObservable(data$, {
get: () => api.getData(),
persist: { name: 'data', plugin: ObservablePersistLocalStorage }
})
// Show loading indicator
observe(() => {
if (syncState$.isLoading.get()) {
showSpinner()
} else {
hideSpinner()
}
})
// Show save status
observe(() => {
if (syncState$.isSetting.get()) {
console.log('Saving...')
} else {
console.log('All changes saved')
}
})
// Handle errors
observe(() => {
const error = syncState$.error.get()
if (error) {
showErrorMessage(error.message)
}
})
Manual Sync Control
const data$ = observable({ items: [] })
const syncState$ = syncObservable(data$, {
get: () => api.getItems(),
syncMode: 'manual', // Don't sync automatically
persist: { name: 'items', plugin: ObservablePersistLocalStorage }
})
// Sync on button click
button.onclick = () => {
syncState$.sync()
}
// Or with refresh
refreshButton.onclick = () => {
syncState$.sync({ resetLastSync: true })
}
Conditional Syncing
const data$ = observable({ value: 0 })
const syncState$ = syncObservable(data$, {
get: () => api.getData(),
persist: { name: 'data', plugin: ObservablePersistLocalStorage }
})
// Disable syncing during offline mode
const isOnline$ = observable(navigator.onLine)
window.addEventListener('online', () => isOnline$.set(true))
window.addEventListener('offline', () => isOnline$.set(false))
observe(() => {
syncState$.isSyncEnabled.set(isOnline$.get())
})
// Manually sync when coming back online
observe(() => {
if (isOnline$.get()) {
syncState$.sync()
}
})
Pending Changes Tracking
const form$ = observable({ name: '', email: '' })
const syncState$ = syncObservable(form$, {
set: ({ value }) => api.saveForm(value),
debounceSet: 1000,
persist: { name: 'form', plugin: ObservablePersistLocalStorage, retrySync: true }
})
// Show unsaved changes indicator
observe(() => {
const pending = syncState$.getPendingChanges()
const hasPending = pending && Object.keys(pending).length > 0
unsavedIndicator.style.display = hasPending ? 'block' : 'none'
})
// Show what's pending
button.onclick = () => {
const pending = syncState$.getPendingChanges()
console.log('Unsaved changes:', pending)
}
Syncing Multiple Observables
const user$ = observable({ name: '', email: '' })
const settings$ = observable({ theme: 'light', notifications: true })
const userSync$ = syncObservable(user$, {
get: () => api.getUser(),
set: ({ value }) => api.saveUser(value),
persist: { name: 'user', plugin: ObservablePersistLocalStorage }
})
const settingsSync$ = syncObservable(settings$, {
get: () => api.getSettings(),
set: ({ value }) => api.saveSettings(value),
persist: { name: 'settings', plugin: ObservablePersistLocalStorage }
})
// Wait for both to load
await Promise.all([
when(userSync$.isLoaded),
when(settingsSync$.isLoaded)
])
Reset and Clear Data
const data$ = observable({ items: [] })
const syncState$ = syncObservable(data$, {
get: () => api.getData(),
persist: { name: 'data', plugin: ObservablePersistLocalStorage }
})
// Clear persisted data and metadata
logoutButton.onclick = async () => {
await syncState$.resetPersistence()
data$.set({ items: [] }) // Reset observable
}
Differences from synced()
| Feature | syncObservable() | synced() |
|---|
| Input | Existing observable | Creates new observable |
| Return | Sync state observable | Synced observable |
| Use Case | Add sync to existing observable | Create synced observable |
| Control | Full control over observable | Observable created for you |
// syncObservable - you control the observable
const data$ = observable({ items: [] })
const syncState$ = syncObservable(data$, options)
// synced - observable is created for you
const data$ = synced(options)
See Also