Skip to content

Events

λ Query has a rich event system that fires during every cache lifecycle operation. You can subscribe to events for reactive updates, testing synchronization, and cross-tab communication.

EventFires WhenDetail
refetchingA fetch starts (initial or background revalidation).Promise<T> — the pending fetch promise.
resolvedA fetch completes successfully.T — the resolved item.
mutatingA mutation starts.Promise<T> — the pending mutation promise.
mutatedA mutation completes.T — the mutated item.
abortedA fetch is cancelled via abort() or forget().The abort reason.
forgottenA key is removed from the cache.T — the forgotten item.
hydratedA key is hydrated directly into the cache.T — the hydrated item.
errorA background refetch fails.The error that was thrown.

Use subscribe() to listen for events on a specific key. It returns an unsubscribe function:

const unsubscribe = query.subscribe("/api/user", "resolved", function (event) {
console.log("User data updated:", event.detail);
});
// Later, when you're done listening:
unsubscribe();

Immediate Notification for In-Flight Requests

Section titled “Immediate Notification for In-Flight Requests”

When subscribing to refetching, if there is already an in-flight request for that key, the event fires immediately with the existing promise:

// A fetch is already in progress for /api/posts.
query.query("/api/posts");
// This subscriber is immediately notified of the in-flight request.
query.subscribe("/api/posts", "refetching", function (event) {
console.log("Fetch in progress:", event.detail);
});

Use once() to wait for the next occurrence of an event. Returns a promise that resolves with the event:

const event = await query.once("/api/user", "resolved");
console.log("User resolved:", event.detail);

once() automatically unsubscribes after receiving the first event. This is especially useful in tests where you need to wait for a specific cache operation.

For waiting on refetching events, prefer using next() instead, which is more ergonomic:

// Wait for the next resolved value.
const result = await query.next("/api/user");
console.log("Next fetch result:", result);

The next() method waits for the next fetch to complete on one or more keys and returns the resolved values directly. This is the preferred way to synchronize with cache updates in tests:

// Wait for a single key.
const user = await query.next("/api/user");
// Wait for multiple keys simultaneously.
const [posts, user] = await query.next(["/api/posts", "/api/user"]);

When you call next(), λ Query subscribes to the refetching event, awaits the fetch promise, and returns the resolved value.

Use sequence() to get an async generator that yields events indefinitely. Break out of the loop when done:

const events = query.sequence("/api/user", "resolved");
for await (const event of events) {
console.log("User updated:", event.detail);
if (shouldStop) break;
}

The stream() method is like next() but yields values continuously as an async generator:

// Stream a single key.
const values = query.stream("/api/user");
for await (const user of values) {
console.log("User:", user);
}
// Stream multiple keys.
const multi = query.stream(["/api/posts", "/api/user"]);
for await (const [posts, user] of multi) {
console.log("Posts:", posts, "User:", user);
}

You can manually emit events using the low-level emit() function. This is an advanced API — λ Query’s methods already emit events internally:

query.emit("/api/user", "resolved", userData);

Use this for testing or advanced scenarios where you need to simulate cache events.

λ Query can synchronize cache events across browser tabs using the BroadcastChannel API. When configured, the following events are broadcast:

  • mutated
  • resolved
  • hydrated
  • forgotten
const query = createQuery({
broadcast: new BroadcastChannel("query"),
});

On the receiving end, call subscribeBroadcast() to replay incoming events:

const unsubscribe = query.subscribeBroadcast();
// Later:
unsubscribe();

When using the React QueryProvider, the broadcast channel is automatically set up and cleaned up for you. You don’t need to manage it manually.

When a mutated, resolved, hydrated, or forgotten event occurs, λ Query posts a message to the broadcast channel with the key and event details. Other tabs receiving the message replay the event on their local event system, keeping all tabs in sync.

For example, if a user updates their profile in one tab, the mutated event propagates to all other tabs, causing any useQuery("/api/user") hooks to update automatically.