4

Currently trying to refactor our project to use Server components (app directory), and the first challenge is how to implement infinite scroll pagination with new "app" directory.

This is an oversimplified page example:

import OrganisationInterface from "@/interfaces/OrganisationInterface";

async function getData() {
  const res = await fetch('http://api.test/v1/organisations?page=1');

  if (!res.ok) {
    throw new Error('Failed to fetch data');
  }

  return res.json();
}

export default async function Page() {
  const { data } = await getData();

  return (
      <>
        <div className="mx-auto in-h-screen ">
          {data && data.map((organisation: OrganisationInterface) => (
              <div key={organisation.id}>
                {organisation.attributes.title.lt}
              </div>
          ))}
        </div>
      </>
  );
}

I prefetch 10 initial results on the server, now I need to make client-side request to add another 10,20,30...

Or should I somehow do it on the server side? I need some ideas or examples of how to implement this correctly, previous solution was based on client-side exclusively, with SWR or ReactQuery.

RomkaLTU
  • 3,683
  • 8
  • 40
  • 63
  • You need to know the scroll position, so I don't think you can do it with only server components. – Youssouf Oumar May 16 '23 at 21:06
  • Yes of course, I need somehow combine server component fetch and client side fetch. – RomkaLTU May 16 '23 at 21:44
  • What about using something like react query's `useInfiniteQuery` on a client component and provide the server data as `initialData`? – ivanatias May 16 '23 at 23:46
  • By passing it as props, I guess. Yeah, it seems ok to me. – Youssouf Oumar May 17 '23 at 05:41
  • @YoussoufOumar, to be able to pass a function as a prop from a server component down to a client component, you'll have to enable experimental server actions (which are currently in alpha). It doesn't "just work" this way out of the box. – Igor Danchenko May 17 '23 at 11:26

1 Answers1

2

You can definitely pre-render on the server with the initial batch of items, and then fetch additional items on the client side as the user scrolls through the page.

Here are several ideas with specific examples.

  1. Implement the data retrieval logic under an API route, and fetch data from that API route in both server and client components.

Sandbox - https://codesandbox.io/p/sandbox/adoring-browser-vk5k0d?file=%2Fapp%2Fpage.tsx

  1. Implement data fetching function as a server action (disclaimer: server actions are currently in alpha), and pass it down to the client component.
async function fetchItems() {
  "use server";
    
  return ...
}

export default async function Home() {    
  const items = await fetchItems();
    
  return <Items initialItems={items} fetchItems={fetchItems} />;
}

Sandbox - https://codesandbox.io/p/sandbox/stackoverflow-76266563-server-actions-f2gt3o?file=%2Fapp%2Fpage.tsx

Igor Danchenko
  • 1,980
  • 1
  • 3
  • 13
  • Your Items component is marked as 'use client', which means it gets rendered on the client, what if I want to render the initial list on the server and the rest on the client? – Gurleen Sethi Jun 21 '23 at 09:09
  • @GurleenSethi, that's exactly how it works. The "use client" annotation doesn't mean that a component runs exclusively on the client. Client components render both server-side (SSR) and client-side. – Igor Danchenko Jun 21 '23 at 14:03
  • Ha, didn't know that tried it and it works, I thought client components only rendered on the client. Do you have a reference to any documentation that explains this behaviour in a bit more detail? – Gurleen Sethi Jun 21 '23 at 19:45
  • 1) Next.js docs - https://nextjs.org/docs/getting-started/react-essentials 2) Google - https://www.google.com/search?q=next.js+ssr+vs+rsc – Igor Danchenko Jun 21 '23 at 20:36