Home  >  Q&A  >  body text

Next.js: Update server component after client component modifies data

I'm still trying to understand this scenario. Can anyone suggest what is the correct way to do this in Next.js 13?

I display 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>
  )
}

On the same page I also defined the 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 in the server component page, as shown below:

// 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 show new/updated users?

P粉832212776P粉832212776381 days ago780

reply all(2)I'll reply

  • P粉904405941

    P粉9044059412023-10-27 13:25:50

    https://stackoverflow.com/a/75127011/17964403 This is great for mutating on the client side, but if you want to do something like searching/filtering using input from the client, 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}`)
    }

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

    reply
    0
  • P粉345302753

    P粉3453027532023-10-27 12:33:01

    To reflect data updated by the client component on the server component, you can use router.refresh(), where router is useRouter(). Here's an example of using a to-do list app:

    // app/page.tsx
    
    import Todo from "./todo";
    async function getTodos() {
      const res = await fetch("https://api.example.com/todos", { cache: 'no-store' });
      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>
      );
    }
    
    // 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>
      );
    }

    ⚠️: The crawl request is cached. That's why cache: 'no-store' in this example.

    reply
    0
  • Cancelreply