Skip to content
Sync

useOfflineFirst

Reactive offline-first data binding powered by Legend-State's sync engine.

Combines remote sync with local persistence and automatic retry. Data is available immediately from local cache, then updated from the remote source. Failed remote operations are persisted locally and retried automatically.

useOfflineFirst
Loading...
Syncing...

Local cache + remote sync with automatic retry.

0
import {
import useOfflineFirst
useOfflineFirst
} from "@usels/web";
import {
class ObservablePersistLocalStorage
ObservablePersistLocalStorage
} from "@usels/web";
interface
interface Settings
Settings
{
Settings.theme: string
theme
: string;
Settings.language: string
language
: string;
}
function
function Component(): JSX.Element
Component
() {
const {
const data$: any
data$
,
const isLoaded$: any
isLoaded$
,
const isPersistLoaded$: any
isPersistLoaded$
,
const refetch: any
refetch
} =
import useOfflineFirst
useOfflineFirst
<
interface Settings
Settings
>({
get: () => any
get
: () =>
any
fetch
("/api/settings").
any
then
((
r: any
r
) =>
r: any
r
.
any
json
()),
set: ({ value }: {
value: any;
}) => any
set
: ({
value: any
value
}) =>
any
fetch
("/api/settings", {
method: string
method
: "PUT",
body: any
body
:
any
JSON
.
any
stringify
(
value: any
value
),
}),
persistKey: string
persistKey
: "settings",
persistPlugin: typeof ObservablePersistLocalStorage
persistPlugin
:
class ObservablePersistLocalStorage
ObservablePersistLocalStorage
,
initial: {
theme: string;
language: string;
}
initial
: {
theme: string
theme
: "light",
language: string
language
: "en" },
});
return (
<
JSX.IntrinsicElements.div: DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div
>
{
const isPersistLoaded$: any
isPersistLoaded$
.
any
get
() ? (
<>
<
JSX.IntrinsicElements.span: DetailedHTMLProps<HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>
span
>{
const isLoaded$: any
isLoaded$
.
any
get
() ? "synced" : "from cache"}</
JSX.IntrinsicElements.span: DetailedHTMLProps<HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>
span
>
<
JSX.IntrinsicElements.span: DetailedHTMLProps<HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>
span
>{
const data$: any
data$
.
any
theme
.
any
get
()}</
JSX.IntrinsicElements.span: DetailedHTMLProps<HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>
span
>
<
JSX.IntrinsicElements.button: DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>
button
DOMAttributes<HTMLButtonElement>.onClick?: MouseEventHandler<HTMLButtonElement> | undefined
onClick
={() =>
const data$: any
data$
.
any
theme
.
any
set
("dark")}>Dark mode</
JSX.IntrinsicElements.button: DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>
button
>
<
JSX.IntrinsicElements.button: DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>
button
DOMAttributes<HTMLButtonElement>.onClick?: MouseEventHandler<HTMLButtonElement> | undefined
onClick
={
const refetch: any
refetch
}>Sync now</
JSX.IntrinsicElements.button: DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>
button
>
</>
) : (
<
JSX.IntrinsicElements.div: DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div
>Loading cache...</
JSX.IntrinsicElements.div: DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div
>
)}
</
JSX.IntrinsicElements.div: DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div
>
);
}
import { useOfflineFirst } from "@usels/web";
import { ObservablePersistLocalStorage } from "@usels/web";
function Component() {
const { data$, clearPersist } = useOfflineFirst<string[]>({
get: () => fetch("/api/items").then((r) => r.json()),
persistKey: "items",
persistPlugin: ObservablePersistLocalStorage,
initial: [],
retry: {
infinite: false,
backoff: "constant",
maxDelay: 5000,
},
});
}
ParameterTypeDescription
optionsOfflineFirstOptions<T>- Offline-first sync configuration.
OptionTypeDefaultDescription
get() => T | Promise<T>-Function that fetches remote data.
set((params: { value: T; changes: object[]; }) => void | Promise<void>)-Function that sends changes to the remote.
initialT-Initial value before data is loaded from persist or remote.
persistKeystring-Storage key for local persistence.
persistPluginObservablePersistPlugin | ClassConstructor<ObservablePersistPlugin, any[]> | undefined-Legend-State persist plugin to use as the storage backend.
mode"set" | "assign" | "merge" | "append" | "prepend""set"How incoming data merges with existing state.
debounceSetnumber-Milliseconds to debounce before sending changes to remote.
transform{ load?: ((value: any) => T) | undefined; save?: ((value: T) => any) | undefined; }-Transform data on load/save.
retry{ infinite?: boolean | undefined; backoff?: "constant" | "exponential" | undefined; maxDelay?: number | undefined; }{ infinite: true, backoff: "exponential", maxDelay: 30000 }Retry configuration for failed remote operations.

OfflineFirstReturn<T>

NameTypeDescription
data$Observable<T>The synced observable data.
isLoaded$ReadonlyObservable<boolean>Whether the remote data has been loaded.
isFetching$ReadonlyObservable<boolean>Whether a fetch (initial or refetch) is currently in progress.
isPersistLoaded$ReadonlyObservable<boolean>Whether locally persisted data has been loaded.
error$ReadonlyObservable<Error | undefined>The most recent sync error, if any.
lastSync$ReadonlyObservable<number | undefined>Timestamp of the last successful sync.
refetch() => voidTrigger a manual re-fetch from remote.
clearPersist() => voidClear all locally persisted data.

View on GitHub