Optimistic Updates
Sometimes waiting for the server is fine. Sometimes it makes the UI feel late.
In our example, the create-event form can stay simple with revalidate(...), but RSVP is different. The user stays on the page and expects the attendee count to change immediately.
That is where Nano Kit Query optimistic updates help. The attendee count can change before the server responds.
Entities
Section titled “Entities”There is one more problem to solve before writing the optimistic mutation. The same event record can appear in several cache entries: the detail query, the main list, and filtered lists. Updating only one cache entry would leave the UI inconsistent.
Nano Kit Query solves this with entities. An entity is a shared cache record that query results can reference. Instead of updating every list and detail cache separately, update the entity once.
The app needs one entity for events:
import { entities, entity } from '@nano_kit/query'
export const EventEntity = entity<BoardEvent>('event')Event detail results can be mapped into that entity:
query( EventKey, [$slug], fetchEvent, [entities(EventEntity)])The list query can map every event inside every infinite page too:
infinite( EventsKey, [$pacedQ, $category], lastPage => lastPage.nextCursor, (q, category, cursor) => fetchEvents({ q, category, cursor }), [ entities(data => ({ ...data, pages: data.pages.map(page => ({ ...page, events: page.events.map(EventEntity) })) })) ])The mutation also needs $data from the query client:
import { client, infinites, mutations } from '@nano_kit/query'
export const { query, infinite, mutation, $data} = client( infinites(), mutations())RSVP can now update one entity immediately, then keep it or roll it back:
export const [rsvp, $rsvpEvent, $rsvpError, $rsvpLoading] = mutation<[id: string], BoardEvent>( (id, ctx) => { const key = EventEntity(id) const previousEvent = $data(key)
if (previousEvent) { $data(key, { ...previousEvent, attendees: previousEvent.attendees + 1 })
onError(ctx, () => { $data(key, previousEvent) }) }
return rsvpEvent(id) })$data updates the normalized entity immediately. onError restores the previous value if the request fails.
EventEntity(id) points to one shared event record in cache. RSVP updates that one record, and every query result that references it sees the new attendees count.