Next.js Discord

Discord Forum

Infinite scrolling with server components

Unanswered
Turkish Angora posted this in #help-forum
Open in Discord
Original message was deleted.

35 Replies

Avatar
Turkish Angora
Oh actually, maybe I have a <SearchResults /> server component, which has a number of these pages.
Because I know I can pass props from server component to server component
Although I am not sure I will be able to add the animation
Have to wrap it in the client component pattern thing
Getting complicated
Avatar
Cape lion
Well, the pattern is using a client component
I am not sure if you can achieve this server side only
I am almost certain you can’t
Just use a combo of tanstack query and next
That should do the trick
Avatar
Cape lion
I mean you already need a client component where you get the scroll position and fetch accordingly. You could have your server data pasesed to the client component and refetch when a certain div slides into view for example ( fetch the next page)
I really don’t see any viable other solution than this
Avatar
Turkish Angora
Well, I already have the pattern in my mind. I have an implementation in react 18
I see a way to do it
The trigger client component simply increments the page count when a new page needs to be fetched and updates the URL
Avatar
Cape lion
Yeah exactly
Avatar
Turkish Angora
The <SearchResults /> .map()s the <SearchResultsPage page={page} />. I have already discovered a server component can pass props to another server component, so that’s fine
I already have it in tanstack, would prefer to use server components
But probably the entire <searhReults> will be re-rendered? So maybe no good
Avatar
Cape lion
Well, i will try this myself i am curious
Avatar
Turkish Angora
Ok
If I can get it to cache pages, which I believe it does out of the box, should be ok.
It’s just then a pain to wrap the server component in the client component using the pattern in the docs.
Really wish there were some syntactic sugar for that
Avatar
Turkish Angora
Basically working, some cache problem
export default async function Page(props: { searchParams: SearchParams }) {
  const searchParams = await props.searchParams;
  const q = searchParams ? searchParams['q'] : undefined;
  const page = searchParams && searchParams['page'] ? +searchParams['page'] : 0;

  return (
    <Centred className="flex flex-col gutterless:pt-12 transition-all">
      <SearchCard />
      {typeof q === 'string' && (
        <>
          {Array.from({ length: page + 1 }, (_, index) => (
            <Suspense key={index} fallback={<Busy />}>
              <SearchResults q={q} page={index} />
            </Suspense>
          ))}
        </>
      )}
      <SearchTrigger />
    </Centred>
  );
};
const SearchTrigger = () => {
  const { ref, inView } = useInView();
  const searchParams = useSearchParams();
  const pathname = usePathname();
  const router = useRouter();

  useEffect(() => {
    if (inView) {
      const currentPage = searchParams.get('page') ? parseInt(searchParams.get('page') as string, 10) : 0;
      const nextPage = currentPage + 1;
      const newSearchParams = new URLSearchParams(searchParams);
      newSearchParams.set('page', nextPage.toString());

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

  return <div ref={ref} className={'h-2'} />
}
It appears to want to re-fetch the <SearchResults />, not cacheing it
And it jumps back to the top, same issue probably
How might I prevent the trigger from triggering while the Page is suspended?
Because at the moment, each re-render reinvokes the trigger
Avatar
Turkish Angora
Sorted it.
export default async function Page(props: { searchParams: SearchParams }) {
  const searchParams = await props.searchParams;
  const q = searchParams ? searchParams['q'] : undefined;
  const page = searchParams && searchParams['page'] ? +searchParams['page'] : 0;

  return (
    <Centred className="flex flex-col gutterless:pt-12 transition-all">
      <SearchCard />
      {typeof q === 'string' && (
        <>
          {[...Array(page + 1)].map((_, index, arr) => (
            <Suspense key={index} fallback={<Busy />}>
              <>
                <SearchResults q={q} page={index} />
                {index === arr.length - 1 && <SearchTrigger />}
              </>
            </Suspense>
          ))}
        </>
      )}
    </Centred>
  );
};
Actually not. Not cacheing still. Wonder why.
Avatar
Turkish Angora
This is becoming awkward. I think I might have to revert to calling an API