From a7762f2583fe03585eabf525d7a9eb63c27a12f1 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 20 Feb 2026 08:07:08 +0000 Subject: [PATCH] fix: stabilize relayHints in useProfile to prevent fetch abort loop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit relayHints was used directly in the useEffect dependency array, so callers passing a new array literal (e.g. [p.relay]) on every render caused the effect to re-run each cycle — aborting the previous network fetch before it could complete. The IndexedDB fast-path masked this in the feed view (profiles already cached), but the detail view showed raw pubkeys because profiles were never fetched from the network. Wrap relayHints in a JSON.stringify-based useMemo (same pattern as useStableArray) so the effect only re-runs when the actual relay values change. https://claude.ai/code/session_01XjwLaShFSVPR5gNA7iUjuB --- src/hooks/useProfile.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/hooks/useProfile.ts b/src/hooks/useProfile.ts index 27f02f3..45a7d5c 100644 --- a/src/hooks/useProfile.ts +++ b/src/hooks/useProfile.ts @@ -1,4 +1,4 @@ -import { useState, useEffect, useRef } from "react"; +import { useState, useEffect, useRef, useMemo } from "react"; import { profileLoader } from "@/services/loaders"; import { ProfileContent, getProfileContent } from "applesauce-core/helpers"; import { kinds } from "nostr-tools"; @@ -22,6 +22,14 @@ export function useProfile( const [profile, setProfile] = useState(); const abortControllerRef = useRef(null); + // Stabilize relayHints so callers can pass [p.relay] without causing + // the effect to re-run (and abort in-flight fetches) every render. + const stableRelayHints = useMemo( + () => relayHints, + // eslint-disable-next-line react-hooks/exhaustive-deps + [JSON.stringify(relayHints)], + ); + useEffect(() => { if (!pubkey) { setProfile(undefined); @@ -45,7 +53,8 @@ export function useProfile( const sub = profileLoader({ kind: kinds.Metadata, pubkey, - ...(relayHints && relayHints.length > 0 && { relays: relayHints }), + ...(stableRelayHints && + stableRelayHints.length > 0 && { relays: stableRelayHints }), }).subscribe({ next: async (fetchedEvent) => { if (controller.signal.aborted) return; @@ -85,7 +94,7 @@ export function useProfile( controller.abort(); sub.unsubscribe(); }; - }, [pubkey, relayHints]); + }, [pubkey, stableRelayHints]); return profile; }