Examples
SWR
Ferry and SWR in one app, each with its own job:
- Ferry holds the rows the app creates, edits and syncs. They are read locally, with no loading state.
- SWR fetches what lives only on a server: another API, or numbers the server computes from the synced rows.
It uses the schema and client of the Quick start. SWR needs no provider, so the
app only has Ferry's SyncProvider.
Local rows next to fetched data
Ferry's hook is useQuery, or useLiveQuery if the name reads better next to useSWR:
import { useLiveQuery } from 'ferrysync/react';
import useSWR from 'swr';
import { sync } from './sync';
export const Trip = ({ projectId }: { projectId: string }) => {
// Local: there on the first render, offline too.
const tasks = useLiveQuery(sync.tasks.where({ projectId, done: false }));
// Remote: fetched, cached, and loading until it answers.
const weather = useSWR(['weather', projectId], () => fetchWeather());
return (
<section>
<p>{weather.isLoading ? 'Checking the weather…' : weather.data}</p>
<ul>
{tasks.map((task) => (
<li key={task.id}>{task.title}</li>
))}
</ul>
</section>
);
};Data the server computes from synced rows
A report the server builds from the tasks changes when the tasks reach the server. Put lastSyncId
in the key: it grows only when the device catches up with the server's log, so the report refetches
when the server may have news, and not on every render or every local write.
import { useSyncStatus } from 'ferrysync/react';
import useSWR from 'swr';
import { sync } from './sync';
export const Report = ({ projectId }: { projectId: string }) => {
const { lastSyncId } = useSyncStatus(sync);
const report = useSWR(['report', projectId, lastSyncId], () => fetchReport(projectId), {
// Keep showing the last report while the next one loads.
keepPreviousData: true,
});
if (report.data === undefined) {
return <p>Loading the report…</p>;
}
return (
<p>
{report.data.done} done, {report.data.open} open
</p>
);
};Gotchas
- Do not wrap Ferry's reads in
useSWR. It adds a second cache that every write and every change from another device would have to revalidate, and a loading state for data that is already in memory. - Do not write through
useSWRMutation.sync.tasks.update(…)applies at once and waits in the outbox when offline. - SWR revalidates on focus; Ferry does not need to. Its realtime connection keeps the rows
current. To push and pull at once when the app comes back, call
sync.syncNow(): see Status and offline.