From 25b2e274becae07d761911900864b0719badee96 Mon Sep 17 00:00:00 2001 From: "shakespeare.diy" Date: Wed, 18 Feb 2026 01:53:12 -0600 Subject: [PATCH] Prevent individual queries from racing with batch queries useAuthor and useEventStats were firing individual queries simultaneously with the batch hooks (useAuthors, useBatchEventStats), causing a flood of concurrent relay subscriptions. Each NoteCard's useAuthor created its own kind:0 query even though useAuthors in Feed.tsx was about to fetch the same data in a single batched query. Fix: Use useIsFetching to detect when a batch query is in-flight and disable individual queries until the batch completes. The batch seeds individual cache entries, so useAuthor/useEventStats resolve instantly from cache without ever firing their own relay queries. On pages without batch queries (PostDetailPage, NotificationsPage, etc.), batchFetching === 0 immediately, so individual hooks fire normally. Co-authored-by: shakespeare.diy --- src/hooks/useAuthor.ts | 9 ++++++++- src/hooks/useTrending.ts | 8 ++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/hooks/useAuthor.ts b/src/hooks/useAuthor.ts index fe04c825..883cf1c5 100644 --- a/src/hooks/useAuthor.ts +++ b/src/hooks/useAuthor.ts @@ -1,10 +1,14 @@ import { type NostrEvent, type NostrMetadata, NSchema as n } from '@nostrify/nostrify'; import { useNostr } from '@nostrify/react'; -import { useQuery } from '@tanstack/react-query'; +import { useQuery, useIsFetching } from '@tanstack/react-query'; export function useAuthor(pubkey: string | undefined) { const { nostr } = useNostr(); + // If a batch author query (useAuthors) is currently in-flight, wait for + // it to finish and seed our cache instead of firing an individual query. + const batchFetching = useIsFetching({ queryKey: ['authors'] }); + return useQuery<{ event?: NostrEvent; metadata?: NostrMetadata }>({ queryKey: ['author', pubkey ?? ''], queryFn: async ({ signal }) => { @@ -30,5 +34,8 @@ export function useAuthor(pubkey: string | undefined) { }, staleTime: 5 * 60 * 1000, // Keep cached data fresh for 5 minutes retry: 1, + // Don't fire this individual query while a batch is in progress — + // the batch will seed our cache entry when it completes. + enabled: !!pubkey && batchFetching === 0, }); } diff --git a/src/hooks/useTrending.ts b/src/hooks/useTrending.ts index 7ac32f7b..89e1ca85 100644 --- a/src/hooks/useTrending.ts +++ b/src/hooks/useTrending.ts @@ -1,5 +1,5 @@ import { useNostr } from '@nostrify/react'; -import { useQuery, useQueryClient } from '@tanstack/react-query'; +import { useQuery, useQueryClient, useIsFetching } from '@tanstack/react-query'; import type { NostrEvent } from '@nostrify/nostrify'; export interface TrendingTag { @@ -181,6 +181,10 @@ function computeStats(eventId: string, events: NostrEvent[]): EventStats { export function useEventStats(eventId: string | undefined) { const { nostr } = useNostr(); + // If a batch stats query (useBatchEventStats) is currently in-flight, wait + // for it to finish and seed our cache instead of firing an individual query. + const batchFetching = useIsFetching({ queryKey: ['batch-event-stats'] }); + return useQuery({ queryKey: ['event-stats', eventId ?? ''], queryFn: async ({ signal }) => { @@ -199,7 +203,7 @@ export function useEventStats(eventId: string | undefined) { return computeStats(eventId, events); }, - enabled: !!eventId, + enabled: !!eventId && batchFetching === 0, staleTime: 60 * 1000, placeholderData: (prev) => prev, });