From 9df574731225331cfe093efbdf1482f644e50e7d Mon Sep 17 00:00:00 2001 From: Mary Kate Fain Date: Sat, 7 Mar 2026 13:46:18 -0600 Subject: [PATCH] Fix search results flickering between old and new queries When navigating from the sidebar search, the URL param (?q=) updates immediately but the local searchQuery state lagged one render cycle behind (synced via useEffect). During that gap, the old query's results would briefly flash before being cleared. Fix: derive an effectiveQuery directly from the URL search params and use it for all data fetching and conditional rendering. The local searchQuery state is kept only as the controlled input value. Closes #72 --- src/pages/SearchPage.tsx | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/pages/SearchPage.tsx b/src/pages/SearchPage.tsx index 1b0a2e1d..960fad9a 100644 --- a/src/pages/SearchPage.tsx +++ b/src/pages/SearchPage.tsx @@ -96,6 +96,10 @@ export function SearchPage() { // Local input state for the search field (avoids trimming while typing) const [searchQuery, setSearchQuery] = useState(searchParams.get('q') ?? ''); + + // The effective query used for data fetching — derived directly from URL + // params so it is never one render behind when navigating from the sidebar. + const effectiveQuery = searchParams.get('q') ?? ''; const [filtersOpen, setFiltersOpen] = useState(false); // ── Filter state — all derived from URL params ────────────────────────── @@ -271,7 +275,7 @@ export function SearchPage() { const parts: string[] = bridged.length > 0 ? bridged.map(p => `protocol:${p}`) : ['protocol:nostr']; - if (searchQuery.trim()) parts.push(searchQuery.trim()); + if (effectiveQuery.trim()) parts.push(effectiveQuery.trim()); if (language !== 'global') parts.push(`language:${language}`); const isDedicatedKindQuery = !kindsOverride && (mediaType === 'vines' || mediaType === 'images' || mediaType === 'videos'); if (!isDedicatedKindQuery && !hasKindMediaConflict) { @@ -282,7 +286,7 @@ export function SearchPage() { if (sort === 'hot') parts.push('sort:hot'); else if (sort === 'trending') parts.push('sort:trending'); return parts.join(' '); - }, [searchQuery, language, mediaType, protocols, kindsOverride, hasKindMediaConflict, sort]); + }, [effectiveQuery, language, mediaType, protocols, kindsOverride, hasKindMediaConflict, sort]); // Active filter labels for the summary / empty state hints const activeFilterLabels = useMemo(() => { @@ -330,11 +334,11 @@ export function SearchPage() { // Build a standard NIP-01 TabFilter from the current search state const currentFilter = useMemo(() => { const filter: TabFilter = {}; - if (searchQuery.trim()) filter.search = searchQuery.trim(); + if (effectiveQuery.trim()) filter.search = effectiveQuery.trim(); if (kindsOverride && kindsOverride.length > 0) filter.kinds = kindsOverride; if (authorScope === 'people' && authorPubkeys.length > 0) filter.authors = authorPubkeys; return filter; - }, [searchQuery, kindsOverride, authorScope, authorPubkeys]); + }, [effectiveQuery, kindsOverride, authorScope, authorPubkeys]); const alreadySaved = savedFeeds.some( (f) => JSON.stringify(f.filter) === JSON.stringify(currentFilter), @@ -372,7 +376,7 @@ export function SearchPage() { ? authorPubkeys : undefined; - const { posts, isLoading: postsLoading } = useStreamPosts(searchQuery, { + const { posts, isLoading: postsLoading } = useStreamPosts(effectiveQuery, { includeReplies, mediaType, language, @@ -381,7 +385,7 @@ export function SearchPage() { authorPubkeys: streamAuthorPubkeys, sort, }); - const { data: profiles, isLoading: profilesLoading, followedPubkeys } = useSearchProfiles(activeTab === 'accounts' ? searchQuery : ''); + const { data: profiles, isLoading: profilesLoading, followedPubkeys } = useSearchProfiles(activeTab === 'accounts' ? effectiveQuery : ''); return (
@@ -700,7 +704,7 @@ export function SearchPage() { )} {/* NIP-50 search query debug block */} - {searchQuery.trim() && ( + {effectiveQuery.trim() && ( @@ -732,7 +736,7 @@ export function SearchPage() { ))} - ) : searchQuery.trim() ? ( + ) : effectiveQuery.trim() ? (
- {searchQuery.trim() ? ( + {effectiveQuery.trim() ? ( profilesLoading ? (
{Array.from({ length: 5 }).map((_, i) => (