02examples
Lazy-load by date range
A controlled calendar that only holds what onRangeChange has fetched, merged page by page, with edits written back to the server copy.
The server holds about 300 events spread over sixteen weeks. The calendar holds none of them until it asks: on mount it reports the visible range and the page arrives 350 ms later. Step with ‹ › or switch to month view and watch the status line — every navigation is a request, the count of loaded events only ever grows, and a week you have already seen is drawn from memory while its refresh is in flight. Drag something, leave, come back: the edit is still there, because it was written through to the server.
liveLazy-load by date range
loading
uses
How it works
eventsandonEventsChangemake the calendar controlled: it draws exactly the array it is given and reports every committed change as a full next array. There are no seeds, no built-in persistence and no store of its own. See Controlled state.onRangeChange(start, end)is the read signal. It fires once on mount and then on every view or date change, with the visible range as[start, end).rangePadding={7}widens what it reports by a week on each side, so the fetch for this week already covers the next one and stepping into it shows no gap.EventServer.fetchRangefilters on overlap — an event touching the range is in the page, which is how a cross-midnight event at the edge still appears.- Pages merge, they do not replace.
mergeByIdfolds a page into what is already loaded, keyed by id, so the neighbours you scrolled past stay put and a refetch of a page you edited returns the edit. Requests can complete out of order and the result is the same. - Writes go through the server copy.
onEventsChangeis only ever handed the loaded pages, so a plain overwrite would drop everything off-screen —EventServer.syncdiffs the previous and next arrays instead: an id that vanished is a delete, everything else is an upsert. - The status line is host code.
requestsrecords each range with acountthat staysnullwhile the promise is pending; the live dot reads that. Nothing in kloq knows a request happened.
Take it further
- Replace
fetchRangewith a route handler that takes?from=&to=and returnsCalEvent[]; the merge stays. Pass the two dates throughtoDateOnlyStringif the server thinks in days. - Use
onCommitas the write path instead of the diff. It hands you each change with its type, so a drag is a PATCH and a paste is one batched POST — and a rejection springs the block back. - Evict.
mergeByIdnever forgets; a long session in day view accumulates. Drop events outside the last few reported ranges and the memory stays flat.