Next.js Discord

Discord Forum

Suspense isn't catching my Promise

Answered
XtratoS posted this in #help-forum
Open in Discord
page.tsx
export default async function Page({ searchParams }: PageProps) {
  return (
    <div className="flex flex-col gap-8">
      <SearchInput
        query={searchParams.q || ''}
      />
      <Suspense fallback={<ResultsTableSekelton />}>
        <ResultsTable
          query={searchParams.q || ''}
        />
      </Suspense>
    </div>
  )
}


ResultsTable.tsx
export default async function ResultsTable({ query }: PageProps) {
  const searchResults = await searchChannels(query);
  const exactMatch = searchResults?.find((result) => result.broadcaster_login.toLowerCase() === query.toLowerCase());

  return (
    <BoxContainer>
      {exactMatch && <>
        <Header header="Exact Match" />
        <Row {...exactMatch} />
      </>}
      <Header header="Results" />
      {searchResults?.map((result, index) => (
        <Row key={index} {...result} />
      ))}
    </BoxContainer>
  )
}


actions.ts
'use server';
export async function searchChannels(name: string): Promise<TwitchAPI.SearchChannel[]> {
  const token = (await getCustomSession() as CustomSession).accessToken;
  const result = await fetchSearchChannels({ query: name, token });
  return result;
}


Suspense doesn't actually load the page, the page transitions from a state to another directly, so the sekelton is never shown!
Answered by Cape lion
Firstly, your "searchChannels" does not need to be a server action. That will create an API route (un-necessarily) for it, since ResultsTable is a server component, you can just call that function without needing the "use server"; directive.

Other than that, I don't exactly see any reason why your skeleton wouldn't be showing, I haven't played with suspense boundaries too much yet, so there may be a detail that I'm not seeing.

The only thing I can think of is that your data is being cached, or the fetch is fast enough for it not to be noticed 🙂
View full answer

4 Replies

Cape lion
Firstly, your "searchChannels" does not need to be a server action. That will create an API route (un-necessarily) for it, since ResultsTable is a server component, you can just call that function without needing the "use server"; directive.

Other than that, I don't exactly see any reason why your skeleton wouldn't be showing, I haven't played with suspense boundaries too much yet, so there may be a detail that I'm not seeing.

The only thing I can think of is that your data is being cached, or the fetch is fast enough for it not to be noticed 🙂
Answer
@XtratoS page.tsx ts export default async function Page({ searchParams }: PageProps) { return ( <div className="flex flex-col gap-8"> <SearchInput query={searchParams.q || ''} /> <Suspense fallback={<ResultsTableSekelton />}> <ResultsTable query={searchParams.q || ''} /> </Suspense> </div> ) } ResultsTable.tsx ts export default async function ResultsTable({ query }: PageProps) { const searchResults = await searchChannels(query); const exactMatch = searchResults?.find((result) => result.broadcaster_login.toLowerCase() === query.toLowerCase()); return ( <BoxContainer> {exactMatch && <> <Header header="Exact Match" /> <Row {...exactMatch} /> </>} <Header header="Results" /> {searchResults?.map((result, index) => ( <Row key={index} {...result} /> ))} </BoxContainer> ) } actions.ts ts 'use server'; export async function searchChannels(name: string): Promise<TwitchAPI.SearchChannel[]> { const token = (await getCustomSession() as CustomSession).accessToken; const result = await fetchSearchChannels({ query: name, token }); return result; } Suspense doesn't actually load the page, the page transitions from a state to another directly, so the sekelton is never shown!
Turkish Van
You might want to add key property inside of Your Suspense boundaries.

In general, React will not trigger the Suspense fallback for already visible content.

It might look like this:
const query = searchParams?.q || ""; 

// The rest of the code

<Suspense key={query} fallback={<ResultsTableSekelton />}>
  <ResultsTable
    query={query}
  />
</Suspense>


By specifying key property, You are implicitly saying React that You want to trigger the Suspense fallback every time Your query gets changed.

Reference to the docs: https://react.dev/reference/react/Suspense#resetting-suspense-boundaries-on-navigation
Also, as caoimhe stated above, there is no reason to make searchChannels a Server Action.
Thank you both for your answers, that was really helpful ❤️