2

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?

eagor
  • 9,150
  • 8
  • 47
  • 50

3 Answers3

6

You can achieve this by calling router.refresh() 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.

Igor Danchenko
  • 1,980
  • 1
  • 3
  • 13
0

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.

Muzam Ali
  • 46
  • 5
  • 2
    1) It would be a pretty bad idea to expose db credentials to the client (I’m not even sure that Prisma would even work on the client); 2) It would be pretty wasteful to poll at fixed intervals unless the OP really intends to do so; 3) Long polling is not something that Next.js/Vercel really supports. – Igor Danchenko May 19 '23 at 16:38
0

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.

Antero
  • 1
  • 2
  • This proposal is not going to work since the OP is clearly using the `app` directory, and an attempt to use the `next/router` is going to fail with "NextRouter was not mounted" error. – Igor Danchenko May 19 '23 at 15:49
  • Also your response contains very much outdated info on the state of server components in Next.js (as of May 2023) – Igor Danchenko May 19 '23 at 16:01