From b25c2db89d706e0378f6fd09ec000842f8c1edc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20G=C3=B3mez?= Date: Tue, 24 Mar 2026 11:49:39 +0100 Subject: [PATCH] feat: add useStableRelayFilterMap with structural filter comparison Stabilizes relay filter map references using isFilterEqual per relay instead of JSON.stringify. Avoids serialization overhead for large filter maps with many relays and pubkeys. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/hooks/useStable.ts | 47 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/hooks/useStable.ts b/src/hooks/useStable.ts index d1ad717..806942c 100644 --- a/src/hooks/useStable.ts +++ b/src/hooks/useStable.ts @@ -55,6 +55,53 @@ export function useStableArray(arr: T[]): T[] { * @param filters - Single filter or array of filters * @returns The memoized filter(s) */ +/** + * Stabilize a relay filter map using structural comparison. + * + * Compares relay keys (sorted), then filter content per relay using + * isFilterEqual. Avoids JSON.stringify overhead for large filter maps + * with many relays and pubkeys. + */ +export function useStableRelayFilterMap( + map: Record | undefined, +): Record | undefined { + const prevRef = useRef | undefined>(undefined); + + if (map === undefined && prevRef.current === undefined) return undefined; + if (map === undefined || prevRef.current === undefined) { + prevRef.current = map; + return map; + } + + const prevKeys = Object.keys(prevRef.current).sort(); + const nextKeys = Object.keys(map).sort(); + + if ( + prevKeys.length !== nextKeys.length || + prevKeys.some((k, i) => k !== nextKeys[i]) + ) { + prevRef.current = map; + return map; + } + + for (const key of prevKeys) { + const prevFilters = prevRef.current[key]; + const nextFilters = map[key]; + if (prevFilters.length !== nextFilters.length) { + prevRef.current = map; + return map; + } + for (let i = 0; i < prevFilters.length; i++) { + if (!isFilterEqual(prevFilters[i], nextFilters[i])) { + prevRef.current = map; + return map; + } + } + } + + return prevRef.current; +} + export function useStableFilters(filters: T): T { const prevFiltersRef = useRef(undefined);