From 2e632d4e2d8f0c69a954470456074b9d79eef560 Mon Sep 17 00:00:00 2001 From: Dylan Audius Date: Wed, 27 May 2026 11:43:51 -0700 Subject: [PATCH] fix(search-explore): eliminate per-card remixes-count N+1 in featured contests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rendering the new search-explore page fired ~20 GET /v1/tracks/:id/remixes?limit=0&only_contest_entries=true requests — one per ContestCard in the Featured Remix Contests carousel. Root cause: `useExploreContent` (the hook backing the carousel) reads a static `explore-content.json` listing featured contest track IDs with no entry-count data, so each ContestCard's `useRemixesCount({trackId, isContestEntry: true})` independently fires a count-only request. The cache-priming side-effect that other contest surfaces rely on (`packages/common/src/api/tan-query/events/useAllRemixContests.ts:93-103`, also mirrored in `useUserRemixContests`) wasn't reachable from this surface. Fix: piggy-back on `useAllRemixContests`'s built-in prime by calling it side-effect-only in `FeaturedRemixContestsSection`, gated on the same `inView` as the main fetch. One batched request seeds the remixes-count cache for the top-N active contests; ContestCards then hit cache instead of issuing their own count fetches. Featured contests in the prime batch (the common case — featured is a curated subset of active) drop from N requests to 0. No backend or shared-hook changes; isolated to the carousel section. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../desktop/FeaturedRemixContestsSection.tsx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/web/src/pages/search-explore-page/components/desktop/FeaturedRemixContestsSection.tsx b/packages/web/src/pages/search-explore-page/components/desktop/FeaturedRemixContestsSection.tsx index 1466cbd0624..20444134b71 100644 --- a/packages/web/src/pages/search-explore-page/components/desktop/FeaturedRemixContestsSection.tsx +++ b/packages/web/src/pages/search-explore-page/components/desktop/FeaturedRemixContestsSection.tsx @@ -1,4 +1,4 @@ -import { useExploreContent } from '@audius/common/api' +import { useAllRemixContests, useExploreContent } from '@audius/common/api' import { exploreMessages as messages } from '@audius/common/messages' import { route } from '@audius/common/utils' import { Box } from '@audius/harmony' @@ -10,6 +10,11 @@ import { CONTEST_CARD_WIDTH } from './constants' import { useExploreSectionTracking } from './useExploreSectionTracking' const SKELETON_COUNT = 6 +// Big enough to cover the featured set with headroom — the All Contests +// endpoint primes a per-track entry-count cache as a side-effect (see +// `useAllRemixContests` queryFn). One batched fetch here replaces ~N +// per-card count-only fetches from `useRemixesCount` inside ContestCard. +const PRIME_BATCH_SIZE = 30 export const FeaturedRemixContestsSection = () => { const { ref, inView } = useExploreSectionTracking('Featured Remix Contests') @@ -18,6 +23,12 @@ export const FeaturedRemixContestsSection = () => { enabled: inView }) + // Side-effect: fetch+prime entry counts for the featured contests so + // ContestCard's `useRemixesCount` hits cache instead of firing a + // count-only request per card. The return value is intentionally + // unused — we only care about the priming inside the hook's queryFn. + useAllRemixContests({ pageSize: PRIME_BATCH_SIZE }, { enabled: inView }) + if (isError || (isSuccess && !data?.featuredRemixContests?.length)) { return null }