Fix Activity tab query stability

The follower-rsvps query was unreliable due to:

1. Stale closure: followedPubkeys was captured at render time but the
   queryKey only included the user pubkey, not the actual follow list.
   When the follow list loaded asynchronously, the queryFn could run
   with an empty or stale array. Now the queryKey includes a stable
   hash of the first 20 followed pubkeys so it re-runs when the
   follow list actually changes.

2. followedPubkeys defaulting to []: when followData was undefined
   (still loading), the default [] made hasFollows false, but on the
   next render when followData arrived, the query might not re-fetch
   because the stale cached result from the empty run was returned.
   Now followedPubkeys is undefined until loaded, and isReady properly
   gates the query.

The React warning about setState during render in useLayoutOptions is
a known pattern used by all pages and is not specific to EventsFeedPage.
This commit is contained in:
Derek Ross
2026-03-01 19:07:59 -05:00
parent 7ce754a84a
commit a8afb9cd58
+13 -4
View File
@@ -175,15 +175,24 @@ function ActivitySection() {
const [loginDialogOpen, setLoginDialogOpen] = useState(false);
const { startSignup } = useOnboarding();
const followedPubkeys = followData?.pubkeys ?? [];
const hasFollows = followedPubkeys.length > 0;
const followedPubkeys = followData?.pubkeys;
const hasFollows = !!followedPubkeys && followedPubkeys.length > 0;
const isReady = !!user && hasFollows;
// Stable key that changes when the follow list actually changes
const followsKey = useMemo(
() => followedPubkeys ? followedPubkeys.slice(0, 20).join(',') : '',
[followedPubkeys],
);
// Fetch RSVPs from followed users
const rsvpQuery = useInfiniteQuery({
queryKey: ['follower-rsvps', user?.pubkey ?? ''],
queryKey: ['follower-rsvps', user?.pubkey ?? '', followsKey],
queryFn: async ({ pageParam }) => {
if (!user || !hasFollows) return { rsvps: [], oldestTimestamp: 0, rawCount: 0 };
// Re-read from the outer scope — these are captured at call time, not mount time
if (!followedPubkeys || followedPubkeys.length === 0) {
return { rsvps: [], oldestTimestamp: 0, rawCount: 0 };
}
const signal = AbortSignal.timeout(8000);
const filter: Record<string, unknown> = {