Next.js Discord

Discord Forum

Loading Page on router.push

Unanswered
Alaskan Malamute posted this in #help-forum
Open in Discord
Alaskan MalamuteOP
Hey guys i am relativly new to next.js and was wanderinf if there is a way to set the page back to the loading state on pushing searchparams using router.push. My Code currently looks like this:

-- dashboard/citizens/page.tsx

export default async function CitizensPage({ searchParams }: { searchParams: Promise<{ query?: string; page?: number }> }) {
    const citizens = await getCitizens({ searchParams: await searchParams });

    return (
        <>
            <PageHeader>
                <PageHeader.Title>Personen</PageHeader.Title>
                <PageHeader.Buttons>
                    <SearchQueryInput placeholder={'Person suchen...'} />
                </PageHeader.Buttons>
            </PageHeader>

            <Card className={'p-4'}>
                <Table>
                    <TableHeader>
                        <TableRow>
                            <TableHead>Name</TableHead>
                            <TableHead>Alias</TableHead>
                            <TableHead>Tags</TableHead>
                            <TableHead>Waffenschein gültig bis</TableHead>
                            <TableHead>Telefonnummer</TableHead>
                            <TableHead>Geschlecht</TableHead>
                            <TableHead>Geburtsdatum</TableHead>
                        </TableRow>
                    </TableHeader>
                    <TableBody>
                        {citizens.map((citizen) => (
                            <TableRow key={citizen.id}>

                            </TableRow>
                        ))}
                    </TableBody>
                </Table>
            </Card>
        </>
    );
}


-- SearchQueryInput.tsx

export function SearchQueryInput({ placeholder }: { placeholder?: string }) {
    const router = useRouter();
    const searchParams = useSearchParams();
    const [query, setQuery] = useState(searchParams.get('query') || '');

    const handleSearch = () => {
        if (!query.trim()) return router.push('?');

        const params = new URLSearchParams(searchParams.toString());

        params.set('query', query.trim());

        if (params.has('page')) params.delete('page');

        router.push(`?${params.toString()}`);
    };

    const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
        if (e.key === 'Enter') {
            e.preventDefault();
            handleSearch();
        }
    };

    return (
        <div className="relative flex items-center h-8 text-sm border border-border rounded-md bg-input">
            <input type="text" name="query" className="outline-none px-2" placeholder={placeholder} onKeyDown={handleKeyDown} onChange={(e) => setQuery(e.target.value)} />
            <button type="submit" className="border border-border mx-auto mr-1 p-1 cursor-pointer rounded-md hover:bg-sidebar/10 transition-colors duration-300" onClick={handleSearch}>
                <SearchIcon size={12} className={'text-foreground/80'} />
            </button>
        </div>
    );
}

17 Replies

Do you have a loading.tsx file at the same level than your page.tsx that’s awaiting searchParams?
Alaskan MalamuteOP
I have a loading.tsx at the same level yes but when pushing search params it stays on the old page until the result of getCitizens is awaited.

-- dashboard/citizens/loading.tsx

export default async function CitizensPage({ searchParams }: { searchParams: Promise<{ query?: string; page?: number }> }) {
    await searchParams;

    return (
        <>
            <PageHeader>
                <PageHeader.Title>Personen</PageHeader.Title>
                <PageHeader.Buttons>
                    <SearchQueryInput placeholder={'Person suchen...'} />
                </PageHeader.Buttons>
            </PageHeader>

            <Card className={'p-4 h-full'}>
                <LoadingSpinner />
            </Card>
        </>
    );
}
It works on the first page load.
Alaskan MalamuteOP
Ah ok i miss understood you
Have you thought about moving the search input block into your /dashboard/citizens/layout.tsx
// /dashboard/citizens/layout.tsx
export default async function CitizensLayout({ children }: { children : ReactNode }) {
    // ...
    return (
        <>
            <PageHeader>
                <PageHeader.Title>Personen</PageHeader.Title>
                <PageHeader.Buttons>
                    <SearchQueryInput placeholder={'Person suchen...'} />
                </PageHeader.Buttons>
            </PageHeader>
            { children }                       
        </>
    );
}

// -----------------------------
// /dashboard/citizens/page.tsx
export default async function CitizensPage({ searchParams }: { searchParams: Promise<{ query?: string; page?: number }> }) {
    const citizens = await getCitizens({ searchParams: await searchParams });

    return (        
        <Card className={'p-4'}>
            <Table>
                <TableHeader>
                    <TableRow>
                        <TableHead>Name</TableHead>
                        <TableHead>Alias</TableHead>
                        <TableHead>Tags</TableHead>
                        <TableHead>Waffenschein gültig bis</TableHead>
                        <TableHead>Telefonnummer</TableHead>
                        <TableHead>Geschlecht</TableHead>
                        <TableHead>Geburtsdatum</TableHead>
                    </TableRow>
                </TableHeader>
                <TableBody>
                    {citizens.map((citizen) => (
                        <TableRow key={citizen.id}>

                        </TableRow>
                    ))}
                </TableBody>
            </Table>
        </Card>        
    );
}

// -----------------------------
// /dashboard/citizens/loading.tsx
export default async function Loading() {
    return (
      <Card className={'p-4 h-full'}>
          <LoadingSpinner />
      </Card>
    );
}
*loading.tsx *will replace the contents on page.tsx while it's fetching the data for the new searchParams. And since <SearchQueryInput placeholder={'Person suchen...'} /> is now in the Layout this will persist and keep its own state.
Alaskan MalamuteOP
The problem is still that that when i enter the search input the site still keeps the old state until the callback of getCitizens is back.
Can I see your folder structure?
Alaskan MalamuteOP
Mmm that folder structure works for me, maybe you are trying to do something I do not fully get. For me my page suspends when I change the params and searchParams.
Alaskan MalamuteOP
How to you set your searchparams
I'm just using <Link /> component. But AFAIK both <Link> and router.push use the same APIs under the hood.
Btw, that's not a real project I dump code and stuff to try on there.
Alaskan MalamuteOP
Can you maybe show your contents
Have in mind some things might look messy, and not sure this approach can work for your specific use-case
Alaskan MalamuteOP
It works for you because you also switch the route using the slug