skip to Main Content

I have a server component ChatList, that fetches data from the database and displays it:

// server component

import Link from "next/link";
import db from "@/prisma";

export const ChatList = async () => {
  const chats = await db.chat.findMany();

  return (
    <ul>
      {chats.map((chat) => (
        <li key={chat.id}>
          <Link href={`chat/${chat.id}`}>{chat.name}</Link>
        </li>
      ))}
    </ul>
  );
};

Then I have a server action, that creates new record in the same table:

"use server";

import db from "@/prisma";

export const createChat = async (name: string) => {
  const newChat = await db.chat.create({
    data: { name },
  });
  return newChat;
};

This action is being invoked as form action prop.

Once new record is created, how do I tell the server component ChatList to refetch the data?

3

Answers


  1. This can be achieved by applying polling where your react component keep sending request and looking for any update after some intervals of time. In react you can achieve that using useState and useEffect.

    import { useState, useEffect } from "react";
    import Link from "next/link";
    import db from "@/prisma";
    
    export const ChatList = async () => {
      const [chats, setChats] = useState([]);
    
      const fetchChats = async () => {
        const updatedChats = await db.chat.findMany();
        setChats(updatedChats);
      };
    
      useEffect(() => {
        fetchChats();
    
        const pollingInterval = setInterval(fetchChats, 5000);
    
        return () => {
          clearInterval(pollingInterval);
        };
      }, []);
    
      return (
        <ul>
          {chats.map((chat) => (
            <li key={chat.id}>
              <Link href={`chat/${chat.id}`}>{chat.name}</Link>
            </li>
          ))}
        </ul>
      );
    };

    Another way of doing is using long-polling that will keep the request open.

    Login or Signup to reply.
  2. The solution depends on how your form component calls the createChat function. If it’s on the client side, it’s not feasible to directly tell the server component to refetch the data, as the server component cannot maintain a state or have any live interactions with the client side.

    Your best solution would be to cause the client to refresh the page or to re-render the server component. When this happens, the server component ChatList would re-run and fetch the latest chats.

    In Next.js, one common method of doing this is using the router object’s push method to navigate to the same page, thus causing a re-render. Here’s how you could do this:

    import { useRouter } from 'next/router'
    
    const FormComponent = () => {
      const router = useRouter()
    
      const handleSubmit = async (e) => {
        e.preventDefault()
        const name = // get the chat name from the form inputs
        await createChat(name)
        // Refresh the page by navigating to it again
        router.push(router.asPath)
      }
    
      return (
        <form onSubmit={handleSubmit}>
          {/* your form inputs and submit button here */}
        </form>
      )
    }
    

    In this code, router.push(router.asPath) navigates to the same page, which causes the page to be re-rendered, and thus the server component ChatList will be called again and fetch the updated chats.

    Also, make sure to update your ChatList and createChat as they are not valid server components and server actions. As of my knowledge cutoff in September 2021, Next.js does not support server components. They might support them in the future, but as of now you should use getServerSideProps or getInitialProps for fetching server-side data.

    And server actions should be implemented as API routes (in the pages/api directory), not as separate functions. You would make a POST request to these routes from the client side to create new chats.

    Login or Signup to reply.
  3. You can achieve this by calling router.reefresh() in your form action.

    "use client";
    
    import { useRouter } from "next/navigation";
    
    // ...
    
    const router = useRouter();
    
    // ...
    
    <form
      action={async (formData) => {
        const name = formData.get("name");
        if (typeof name === "string") {
          await createChat(name);
          router.refresh();
        }
      }}
    >
    

    You can also look into experimental useOptimistic hook to make the UI look more responsive.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search