Next.js Discord

Discord Forum

Is there a way to have 'redirect' ALWAYS hard-load a page?

Answered
decorum posted this in #help-forum
Open in Discord
I have an app where there is always a SearchBar present in the AppBar of the Root Layout.

Whenever someone fills out the form associated with the SearchBar, it triggers a Server Action that parses the form into Search Params (Query Params) and then calls redirect('/search?<formatted-and-encoded-search-params>).

This works great! On every other page...

However, when trying to submit a search from the /search page itself, I don't see my loading.tsx file getting triggered and I don't see any indication that a search is being performed until poof the data is changed immediately on the Search Results page.

I've tried adding a template.tsx file to the route, a <Suspense> tag around the Results Table, nothing seems to trigger an indication to the user that data is being fetched on the backend.

Is there a way to enforce that every time I call redirect , even if I'm already at that path, that I hard-load the page?
Answered by joulev
Suspense only renders the loading screen for the first "suspension" (sort of like the stale-while-revalidate approach of data fetching).

you can add a key to the Suspense to force the loading screen:

import { Suspense } from "react";
import { Search } from "./search";

async function Data({ query }: { query?: string }) {
  await new Promise((resolve) => setTimeout(resolve, 2000));
  if (!query) return <div>You didn't search for a query</div>;
  return <div>You searched for {query}</div>;
}

export default function Page({
  searchParams,
}: {
  searchParams: Record<string, string | string[] | undefined>;
}) {
  const query = Array.isArray(searchParams.query)
    ? searchParams.query[0]
    : searchParams.query;
  return (
    <>
      <Search />
      <Suspense fallback={<div>Loading...</div>} key={query}>
        <Data query={query} />
      </Suspense>
    </>
  );
}
View full answer

4 Replies

@decorum I have an app where there is always a SearchBar present in the AppBar of the Root Layout. Whenever someone fills out the form associated with the SearchBar, it triggers a Server Action that parses the form into Search Params (Query Params) and then calls `redirect('/search?<formatted-and-encoded-search-params>)`. This works great! On _every other_ page... However, when trying to submit a search from the `/search` page itself, I don't see my `loading.tsx` file getting triggered and I don't see any indication that a search is being performed until _poof_ the data is changed immediately on the Search Results page. I've tried adding a `template.tsx` file to the route, a `<Suspense>` tag around the Results Table, nothing seems to trigger an indication to the user that data is being fetched on the backend. Is there a way to enforce that _every time_ I call `redirect` , even if I'm already at that path, that I hard-load the page?
Suspense only renders the loading screen for the first "suspension" (sort of like the stale-while-revalidate approach of data fetching).

you can add a key to the Suspense to force the loading screen:

import { Suspense } from "react";
import { Search } from "./search";

async function Data({ query }: { query?: string }) {
  await new Promise((resolve) => setTimeout(resolve, 2000));
  if (!query) return <div>You didn't search for a query</div>;
  return <div>You searched for {query}</div>;
}

export default function Page({
  searchParams,
}: {
  searchParams: Record<string, string | string[] | undefined>;
}) {
  const query = Array.isArray(searchParams.query)
    ? searchParams.query[0]
    : searchParams.query;
  return (
    <>
      <Search />
      <Suspense fallback={<div>Loading...</div>} key={query}>
        <Data query={query} />
      </Suspense>
    </>
  );
}
Answer
I'll give this a shot @joulev
@joulev that worked like a charm! Cheers!

I ended up making a template.tsx file that basically looks like this:
'use client'

import { useSearchParams } from 'next/navigation'
import { ReactNode, Suspense } from 'react'

import Loading from './loading'

export default function Template({ children }: { children: ReactNode }) {
  const searchParams = useSearchParams()

  return (
    <Suspense fallback={<Loading />} key={searchParams.toString()}>
      {children}
    </Suspense>
  )
}


and I put that at the same level as my page.tsx which is doing the data fetching, and it works like a charm!

really appreciate the help!
oh, that's really nice. glad to hear it's working