Next.js Discord

Discord Forum

Infinite scroll with data fetching from Supabase

Unanswered
European anchovy posted this in #help-forum
Open in Discord
European anchovyOP
Is it possible to fetch data from supabase with a server component, so that the user doesn't have to wait so long for to receive that data? Could at least the first "page" of the infinite scroll come from a server component? I am trying to make a Twitter clone. Here is my infinite scroll component, but it's a client component. Any improvements or suggestions are welcome.

11 Replies

European anchovyOP
I managed to fetch the first page in a server component. Is there a way to do this for every fetch request ?
you can use server rendering with server actions: https://github.com/gabrielelpidio/next-infinite-scroll-server-actions
European anchovyOP
@riský thanks for your help I am checking it out now. I think this is better than my approach. I simply made a request to my db in a server component to get the first page and then inside of a client component I am doing all the other requests for the pages. I will try to understand this approach with the server actions and test it out. One new issue that I have now is that when I submit a new tweet it doesn't revalidate the path. I have created a server component to use a form server action to insert my new tweet into my database and there I revalidate the path. In a client component I am receiving that server action as a prop and I await it's response in a handle submit function for a form. If you could please provide some help to figure out why it doesn't revalidate the path when I submit a new tweet, I'd really appreciate it. Thank you in advance.
const ComposeTweetServer = async ({ user }: any) => {
  const submitTweet = async (formData: FormData) => {
    "use server";

    const tweetText = formData.get("tweetText"); // textArea name
    if (!tweetText) return;

    const supabase = createServerActionClient({ cookies });

    // try {
    // TODO: validate insert - tweet content

    const { data, error } = await supabase.from("tweets").insert({
      user_id: user.id,
      text: tweetText.toString(),
    });

    if (error) {
      console.log(error);
    }

    // } catch (error) {
    //   console.log(error);
    // }

    revalidatePath("/home");
    return { data, error };
  };

  return <ComposeTweetClient user={user} serverAction={submitTweet} />;
};

export default ComposeTweetServer;
and from the client component:
const postTweet = async (data: FormData) => {
    if (
      tweetTextRef.current &&
      tweetTextRef.current.value !== "" &&
      tweetTextRef.current.value.length <= tweetMaxLength
    ) {
      try {
        const response = await serverAction(data);
        console.log(response);

        tweetTextRef.current.value = "";
        if (response?.error) {
          console.log(response.error.message);
        }
      } catch (error) {
        console.log(error);
      }
    }
  };

  return (
    <>
        <form
          action={postTweet}
          className="flex flex-col w-full flex-grow px-2 pt-2"
        >
          <div className="flex flex-col w-full">
            <div className="flex flex-col border-b">
              <textarea
                ref={tweetTextRef}
                onChange={handleChange}
                maxLength={characterLimit}
                name="tweetText"
                // add invisible scrollbar
                className="bg-transparent border-none outline-none resize-none pt-2"
                placeholder="What is happening?!"
              />
            </div>

            <div className="flex flex-col items-end mt-4">
              <div className="flex">
                {remainingChars <= 20 && (
                  <p className={`py-2 px-4 text-${remainingCharsColor}`}>
                    {remainingChars}
                  </p>
                )}

                <button
                  type="submit"
                  className="rounded-full bg-blue-500 py-2 px-4"
                  // onClick={postTweet}
                >
                  Post
                </button>
              </div>
            </div>
          </div>
        </form>
there is a quick flickering or something so it might actually revalidate the /home path...but nothing happens....idk why
  const { data, error } = await supabase
    .from("tweets")
    .select("*, author: profiles(*), likes(*)")
    .order("created_at", { ascending: false })
    .limit(10);

  const tweets = data?.map((tweet: any) => ({
    ...tweet,
    user_has_liked: !!tweet.likes.find(
      (like: any) => like.user_id === user?.id
    ),
    likes: tweet.likes.length,
  }));

  return (
    <>
      <MainHeader />
      <ComposeTweetServer user={user} />
      <InfiniteFeed user={user} firstTweetsPage={tweets} />
    </>
  );
this is the home path page.tsx
European anchovyOP
if I remove the infiniteFeed, which is where I fetch my tweets with the scroll... then I can make a new post and the revalidatePath to /home works and it shows the new post on top...but this only works if I fetch all the tweets at once instead of only the first 10 in the server component and the others with the scroll in the InfiniteFeed....why ?
European anchovyOP
I've fixed it. It was a react issue. My bad.
@European anchovy I've fixed it. It was a react issue. My bad.
wait, so using server actions worked? if so YAY!
European anchovyOP
@riský oh no I didn't implement it that way yet. I will try it soon and let you know if it works. For now I am fetching the first page in a server component, the rest of the pages are fetched in a client component and stored there in a react usestate. I have a server component to submit a new tweet and a client component for that as well. The issue was that I revalidate the path in that server submit tweet component and I then have to reset the initial state of my useState which contains the second tweet page and the rest. I fixed that now so it all works as expected and I will try to do it with server actions now because I hope it will be faster and revalidating the path makes some components flicker for a split second idk why.