Suspense only shows its fallback once (on page load), when async component suspends, the Suspense wrapper doesn't fall back. All components working however #50563
-
I'm following the same streaming pattern seen in the app- playground. Essentally I have a app
Here's the import { Suspense } from "react";
import styles from "./Search.module.css";
import SearchMasthead from "./_sections/SearchMasthead";
import SearchResults from "./_sections/SearchResults";
import SearchResultsSkeleton from "./_sections/SearchResultsSkeleton";
export const runtime = "experimental-edge";
export const dynamic = "force-dynamic";
export default function SearchPage({
searchParams,
}: {
searchParams: { [key: string]: string | string[] | undefined };
}) {
const searchQuery = searchParams.query?.toString().toLowerCase();
return (
<>
<main className={styles.container}>
<SearchMasthead />
<Suspense fallback={<SearchResultsSkeleton />}>
{/* @ts-expect-error Async Server Component */}
<SearchResults
data={fetch(
`https://dummyjson.com/products/search?q=${searchQuery}`,
{
cache: "no-store",
}
)}
/>
</Suspense>
</main>
</>
);
} Notice the Here's the import styles from "../Search.module.css";
import SearchProductCard from "./SearchProductCard";
export default async function SearchResults({
data,
}: {
data: Promise<Response>;
}) {
// fake delay of 3 seconds
await new Promise((resolve) => setTimeout(resolve, 3000));
const { products } = (await data.then((res) => res.json())) as any;
return (
<>
<section className={styles.productGridHeader}>
<h2 className={styles.gridTitle}>Products</h2>
<span className={styles.gridCount}>
{products ? products.length : 0} Results
</span>
</section>
<section className={styles.productGrid}>
{products.map((product: any) => {
return (
<SearchProductCard
title={product.title}
thumbnail={"/placeholder-slim.png"}
description={
"Description of the product that contains the keywords..."
}
/>
);
})}
</section>
</>
);
} This works perfectly when the page first loads. However, The only problem i'm running into here is that during that second API call, the Suspense isn't falling back to the skeleton for this second call, the previous results are showing all the way up until they're instantly updated with the new results. I'm relatively new to Nextjs so any clarification on what I should be doing here would be great. I'd prefer to keep this server side and not convert to client components as I'm a bit stubborn that this is the pattern shown in the playground. Edit: One observation I made was that the URL only changes once the API call is completed again, so |
Beta Was this translation helpful? Give feedback.
Replies: 5 comments 10 replies
-
@delbaoliveira I hope you don't mind me tagging you here, as you are the author of the example I'm following 😅 |
Beta Was this translation helpful? Give feedback.
-
I've faced similar issue, and fix the problem when i add key prop to Suspense, like:
suspenseKey could be the unique key relevant to the searchParams. hope it helps! |
Beta Was this translation helpful? Give feedback.
-
Thanks @zhanghaoda for the solution. I've solved it by adding export default async function Page({ searchParams }) {
const params = new URLSearchParams(searchParams);
return (
<Suspense key={params.toString()} fallback={<Loading />}>
{/* fetch data inside Listing*/}
<Listing/>
</Suspense>
);
} Note for googlers: |
Beta Was this translation helpful? Give feedback.
-
can we suspend something in a client component with latest Nextjs? |
Beta Was this translation helpful? Give feedback.
-
Hello everyone, My suspense usage is working perfectly in dev but not after deploying to Vercel... |
Beta Was this translation helpful? Give feedback.
I've faced similar issue, and fix the problem when i add key prop to Suspense, like:
suspenseKey could be the unique key relevant to the searchParams.
hope it helps!