Next.js Discord

Discord Forum

Filtering using search params

Answered
Pumi posted this in #help-forum
Open in Discord
Avatar
PumiOP
Hello!

I'm new to React and Next.js so bear with me, I'm coming from years of Angular.

I want to build a filter where users can search and filter the results of a list, a simple input for now.

This is my page
'use cache';

import { Suspense } from 'react';
import { OrganisationsFilter } from './components/organisations-filter';

export default async function Home({ searchParams }: Readonly<{ searchParams: Promise<{ query: string }> }>) {
    const { query } = await searchParams;

    return (
        <Suspense>
            <main className="p-4">
                <OrganisationsFilter />
            </main>
        </Suspense>
    );
}


And this is my filter component
'use client';

import { useEffect, useState } from 'react';
import { usePathname, useRouter, useSearchParams } from 'next/navigation';
import { useDebounce } from 'use-debounce';

import { Input } from '@/components/ui/input';

export function OrganisationsFilter() {
    const router = useRouter();
    const pathname = usePathname();
    const searchParams = useSearchParams();

    const [query, setQuery] = useState(searchParams.get('query') || '');
    const [debouncedQuery] = useDebounce(query, 500);

    useEffect(() => {
        const searchParams = new URLSearchParams();

        if (debouncedQuery) {
            searchParams.set('query', debouncedQuery);
        }

        router.push(`${pathname}?${searchParams.toString()}`);
    }, [debouncedQuery]);

    return (
        <div>
            <Input placeholder="Sök organisation..." onChange={(e) => setQuery(e.target.value)} />
        </div>
    );
}


The problem is that the page seems to get stuck, and stops updating the url after it has been changed a few times. I don't get any errors though.
Answered by James4u (Tag me if needed)
View full answer

31 Replies

Avatar
Answer
Avatar
PumiOP
Looks promising, out picking up kids at the moment, will get back to you!
Avatar
PumiOP
@James4u (Tag me if needed) This seems to solve the issue with the query params not updating after a while, however, the page is not reloading now as the search params change. Am I supposed to also push to the router to trigger data refetching?
This is my updated code

Filter
'use client';

import { useQueryState } from 'nuqs';

import { Input } from '@/components/ui/input';

export function OrganisationsFilter() {
    const [query, setQuery] = useQueryState('query', { defaultValue: '' });

    return (
        <div>
            <Input placeholder="Sök organisation..." value={query} onChange={(e) => setQuery(e.target.value)} />
        </div>
    );
}



Page
export default async function Home({ searchParams }: Readonly<{ searchParams: Promise<{ query: string }> }>) {
    const { query } = await searchParams;
    const organisations = await db.query.organisationsTable.findMany({
        where: (table, { eq }) => {
            if (query) {
                return eq(table.name, query);
            }

            return undefined;
        },
    });

    return (
        <Suspense>
            <main className="p-4">
                <OrganisationsFilter />

                {organisations.map((org, orgIndex) => (
                    <div key={orgIndex}>{org.name}</div>
                ))}
            </main>
        </Suspense>
    );
}
Avatar
yeah, you need router.refresh()
Avatar
PumiOP
I assume the best course of action is to do that in a useEffect then, and maybe even debounce the value from the useQueryState?
Avatar
yeah, if you don't have submit button - that would be the best choice
and yeah debounce it
Avatar
PumiOP
Am I supposed to have 'use cache'; at the top of such pages?

I get this error otherwise
Error: Route "/": A component accessed data, headers, params, searchParams, or a short-lived cache without a Suspense boundary nor a "use cache" above it. We don't have the exact line number added to error messages yet but you can see which component in the stack below. See more info: https://nextjs.org/docs/messages/next-prerender-missing-suspense
Avatar
hmm are you using canary?
Avatar
PumiOP
I am, yes
I'm starting to think maybe I shouldn't?
check out this, you can wrap your component by <Suspense />
I think it should be better than trying "use cache"
Avatar
PumiOP
But my whole page is wrapped in Suspense, is that wrong maybe and just the filter component should be?
import { Suspense } from 'react';
import { OrganisationsFilter } from './components/organisations-filter';
import { getPublicOrganisationsByNameAction } from '@/lib/actions/organisations/organisations.actions';

export default async function Home({ searchParams }: Readonly<{ searchParams: Promise<{ query: string }> }>) {
    const { query } = await searchParams;
    const organisations = await getPublicOrganisationsByNameAction(query);

    return (
        <Suspense fallback={<div>Laddar...</div>}>
            <main className="p-4">
                <OrganisationsFilter />

                {organisations.map((org, orgIndex) => (
                    <div key={orgIndex}>{org.name}</div>
                ))}
            </main>
        </Suspense>
    );
}
With this I am still getting the error
Error: Route "/": A component accessed data, headers, params, searchParams, or a short-lived cache without a Suspense boundary nor a "use cache" above it. We don't have the exact line number added to error messages yet but you can see which component in the stack below. See more info: https://nextjs.org/docs/messages/next-prerender-missing-suspense
Avatar
I think you should follow this structure
import { Suspense } from 'react'
 
async function TransactionList() {
  const transactions = await db.query(...)
  return ...
}
 
function TransactionSkeleton() {
  return <ul>...</ul>
}
 
export default async function Page() {
  return (
    <Suspense fallback={<TransactionSkeleton />}>
      <TransactionList/>
    </Suspense>
  )
}
Avatar
PumiOP
I see, split things up more
Ill try
Avatar
PumiOP
This seems to work great now. Thanks a lot for the help! "Thinking in react/next.js" is hard coming from ~8 years of angular.
Avatar
haha no worries! great!
Mark solution to close this thread if you don't have any other questions 🙂
wow, amazing! @Pumi You should be a master of Angular
Avatar
PumiOP
Angular is a breeze for me, yes. But I like to experiment with other stuff and next has caught my eye, so I’m giving it a shot!
Avatar
I don't know much about Angulr - but I know you should like Next.js
Avatar
PumiOP
Great! I think I will too when I get into the mental model of it a bit more. Have a good evening! :)
Avatar
you too! I actually want to hear from soneone who mastered two JS frameworks (Vue or Ang, or Remix, Next)
hope you will be the one soon
Avatar
PumiOP
I'll let you know if I do! ;)