fix: stabilize relayHints in useProfile to prevent fetch abort loop

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
This commit is contained in:
Claude
2026-02-20 08:07:08 +00:00
parent a5d0106207
commit a7762f2583

View File

@@ -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<ProfileContent | undefined>();
const abortControllerRef = useRef<AbortController | null>(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;
}