diff --git a/frontends/api/src/ssr/usePrefetchWarnings.test.ts b/frontends/api/src/ssr/usePrefetchWarnings.test.ts index 1b937459e1..1f92c99e29 100644 --- a/frontends/api/src/ssr/usePrefetchWarnings.test.ts +++ b/frontends/api/src/ssr/usePrefetchWarnings.test.ts @@ -1,4 +1,4 @@ -import { renderHook } from "@testing-library/react" +import { renderHook, waitFor } from "@testing-library/react" import { useQuery } from "@tanstack/react-query" import { usePrefetchWarnings } from "./usePrefetchWarnings" import { setupReactQueryTest } from "../hooks/test-utils" @@ -35,6 +35,7 @@ describe("SSR prefetch warnings", () => { initialProps: { queryClient }, }) + await waitFor(() => expect(console.info).toHaveBeenCalledTimes(1)) expect(console.info).toHaveBeenCalledWith( "The following queries were requested in first render but not prefetched.", "If these queries are user-specific, they cannot be prefetched - responses are cached on public CDN.", @@ -97,6 +98,7 @@ describe("SSR prefetch warnings", () => { initialProps: { queryClient }, }) + await waitFor(() => expect(console.info).toHaveBeenCalledTimes(1)) expect(console.info).toHaveBeenCalledWith( "The following queries were prefetched on the server but not accessed during initial render.", "If these queries are no longer in use they should removed from prefetch:", diff --git a/frontends/api/src/ssr/usePrefetchWarnings.ts b/frontends/api/src/ssr/usePrefetchWarnings.ts index 000d80261c..9682576b63 100644 --- a/frontends/api/src/ssr/usePrefetchWarnings.ts +++ b/frontends/api/src/ssr/usePrefetchWarnings.ts @@ -1,5 +1,7 @@ -import { useEffect } from "react" +import { useEffect, useState } from "react" import type { Query, QueryClient, QueryKey } from "@tanstack/react-query" +import { useIsFetching } from "@tanstack/react-query" +import { useMounted } from "./useMounted" const logQueries = (...args: [...string[], Query[]]) => { const queries = args.pop() as Query[] @@ -17,7 +19,13 @@ const logQueries = (...args: [...string[], Query[]]) => { ) } -const PREFETCH_EXEMPT_QUERIES = [["userMe"]] +const PREFETCH_EXEMPT_QUERIES = [ + ["userMe"], + ["userLists", "membershipList", "membershipList"], + ["learningPaths", "membershipList", "membershipList"], +] + +const RETRIES = process.env.JEST_WORKER_ID ? 1 : 10 /** * Call this as high as possible in render tree to detect query usage on @@ -39,13 +47,28 @@ export const usePrefetchWarnings = ({ */ exemptions?: QueryKey[] }) => { + const mounted = useMounted() + const [count, setCount] = useState(0) + const fetchingCount = useIsFetching() + const [potentialWarnings, setPotentialWarnings] = useState(true) + + useEffect(() => { + if ((potentialWarnings && count < RETRIES) || count === RETRIES - 1) { + setTimeout(() => setCount(count + 1), 250) + } + }, [count, potentialWarnings]) + /** * NOTE: React renders components top-down, but effects run bottom-up, so * this effect will run after all child effects. */ useEffect( () => { - if (process.env.NODE_ENV === "production") { + if ( + process.env.NODE_ENV === "production" || + !mounted || + fetchingCount > 0 + ) { return } @@ -63,7 +86,7 @@ export const usePrefetchWarnings = ({ !query.isDisabled(), ) - if (potentialPrefetches.length > 0) { + if (potentialPrefetches.length > 0 && count === RETRIES) { logQueries( "The following queries were requested in first render but not prefetched.", "If these queries are user-specific, they cannot be prefetched - responses are cached on public CDN.", @@ -80,17 +103,28 @@ export const usePrefetchWarnings = ({ !query.isDisabled(), ) - if (unusedPrefetches.length > 0) { + if (unusedPrefetches.length > 0 && count === RETRIES) { logQueries( "The following queries were prefetched on the server but not accessed during initial render.", "If these queries are no longer in use they should removed from prefetch:", unusedPrefetches, ) } + + setPotentialWarnings( + potentialPrefetches.length > 0 || unusedPrefetches.length > 0, + ) }, // We only want to run this on initial render. // (Aside: queryClient should be a singleton anyway) // eslint-disable-next-line react-hooks/exhaustive-deps - [], + [mounted, fetchingCount, count], ) } + +const PrefetchWarnings = ({ queryClient }: { queryClient: QueryClient }) => { + usePrefetchWarnings({ queryClient }) + return null +} + +export default PrefetchWarnings diff --git a/frontends/main/src/app/providers.tsx b/frontends/main/src/app/providers.tsx index 5e2908d7c9..144dd0e9e7 100644 --- a/frontends/main/src/app/providers.tsx +++ b/frontends/main/src/app/providers.tsx @@ -10,16 +10,21 @@ import { } from "ol-components" import { Provider as NiceModalProvider } from "@ebay/nice-modal-react" import ConfiguredPostHogProvider from "@/page-components/ConfiguredPostHogProvider/ConfiguredPostHogProvider" -import { usePrefetchWarnings } from "api/ssr/usePrefetchWarnings" import { AppProgressBar as ProgressBar } from "next-nprogress-bar" import type { NProgressOptions } from "next-nprogress-bar" +import dynamic from "next/dynamic" +import { usePrefetchWarnings } from "api/ssr/usePrefetchWarnings" + +const PrefetchWarnings = dynamic(() => import("api/ssr/usePrefetchWarnings"), { + ssr: false, +}) const PROGRESS_BAR_OPTS: NProgressOptions = { showSpinner: false } export default function Providers({ children }: { children: React.ReactNode }) { const queryClient = getQueryClient() - usePrefetchWarnings({ queryClient }) + usePrefetchWarnings() return ( <> @@ -30,6 +35,7 @@ export default function Providers({ children }: { children: React.ReactNode }) { shallowRouting /> +