skip to Main Content

I am still trying to wrap my head around this scenario. Can anyone please suggest what is the correct way to do this in Next.js 13?

I diplay a list of users in a Server Component, for example, like this (using MongoDB):

// UsersList.jsx
const UsersList = () => {
  const users = await usersCollection.getUsers()

  return (
  <div>
    {users.map(user) => <div>{user}</div>}
  </div>
  )
}

And on the same page, I have also defined client component for adding users:

// UsersEdit.jsx
'use client'
const UsersEdit = () => {
  const handleAdd() => // calls POST to /api/users
  return // render input + button
}

Both are displayed together like this in a Server Component Page:

// page.jsx
const Users = () => {
  return (
  <div>
    <UsersList />
    <UsersEdit />
  </div>
  )
}

How should I "reload" or "notify" UsersList that a new user has been added to the collection to force it to display a new user/updated user?

2

Answers


  1. For now, the only way to have the updated data by your Client Component reflected on the Server Component is to call router.refresh(), where router is the returned value by useRouter, after your request to the API. As you can read on the official Next.js doc:

    The Next.js team is working on a new RFC for mutating data in Next.js. This RFC has not been published yet. For now, we recommend the following pattern:

    You can mutate data inside the app directory with router.refresh().

    And they gave a wonderful example, working with a Todo List application. I added it below to have a more complete thread.

    Let’s consider a list view. Inside your Server Component, you fetch the list of items:

    // app/page.tsx
    
    import Todo from "./todo";
    async function getTodos() {
      const res = await fetch("/api/todos");
      const todos = await res.json();
      return todos;
    }
    
    export default async function Page() {
      const todos = await getTodos();
      return (
        <ul>
          {todos.map((todo) => (
            <Todo key={todo.id} {...todo} />
          ))}
        </ul>
      );
    }
    

    Each item has its own Client Component. This allows the component to use event handlers (like onClick or onSubmit) to trigger a mutation.

    // app/todo.tsx
    
    "use client";
    
    import { useRouter } from 'next/navigation';
    import { useState, useTransition } from 'react';
    
    export default function Todo(todo) {
      const router = useRouter();
      const [isPending, startTransition] = useTransition();
      const [isFetching, setIsFetching] = useState(false);
    
      // Create inline loading UI
      const isMutating = isFetching || isPending;
    
      async function handleChange() {
        setIsFetching(true);
        // Mutate external data source
        await fetch(`https://api.example.com/todo/${todo.id}`, {
          method: 'PUT',
          body: JSON.stringify({ completed: !todo.completed }),
        });
        setIsFetching(false);
    
        startTransition(() => {
          // Refresh the current route and fetch new data from the server without
          // losing client-side browser or React state.
          router.refresh();
        });
      }
    
      return (
        <li style={{ opacity: !isMutating ? 1 : 0.7 }}>
          <input
            type="checkbox"
            checked={todo.completed}
            onChange={handleChange}
            disabled={isPending}
          />
          {todo.title}
        </li>
      );
    }
    
    Login or Signup to reply.
  2. https://stackoverflow.com/a/75127011/17964403
    This is Great for mutating on the client side but if you want to do something like search/filtering using input on the client side and want to re-fetch the same data you can do something like

    const [searchterm, setSearchterm] = useState("");
    const handleSearch = (e) => {
       e.preventDefault();
       if(!searchterm)return
       router.push(/home?search=`${searchterm}`)
    }

    and in the server component, you will receive the search param as a prop, see if the search param exists, if so then in the fetch call pass that and you will get filtered items.

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