Обновить серверный компонент после изменения данных клиентским компонентом в Next.js

Я все еще пытаюсь обдумать этот сценарий. Может кто-нибудь подсказать, как правильно это сделать в Next.js 13?

Я отображаю список пользователей в серверном компоненте, например, так (используя MongoDB):

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

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

И на той же странице я также определил клиентский компонент для добавления пользователей:

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

Оба отображаются вместе, как показано на странице серверных компонентов:

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

Как мне «перезагрузить» или «уведомить» UsersList о том, что в коллекцию добавлен новый пользователь, чтобы принудительно отобразить нового пользователя/обновленного пользователя?

🤔 А знаете ли вы, что...
JavaScript позволяет создавать мобильные приложения для iOS и Android с использованием фреймворков, таких как React Native и NativeScript.


1
71
1

Ответ:

Решено

На данный момент единственный способ отобразить обновленные данные вашим клиентским компонентом на серверном компоненте — это вызвать router.refresh(), где router — это значение, возвращаемое useRouter, после вашего запроса к API. Как вы можете прочитать в официальном документе Next.js :

Команда Next.js работает над новым RFC для изменения данных в Next.js. Этот RFC еще не опубликован. На данный момент мы рекомендуем следующий шаблон:

Вы можете изменить данные внутри каталога приложения с помощью router.refresh().

И они привели замечательный пример, работая с приложением Todo List. Я добавил его ниже, чтобы иметь более полную тему.

Рассмотрим представление списка. Внутри вашего серверного компонента вы получаете список элементов:

// 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>
  );
}

Каждый элемент имеет свой клиентский компонент. Это позволяет компоненту использовать обработчики событий (например, onClick или onSubmit) для запуска мутации.

// 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 handleChangeEvent() {
    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 = {handleChangeEvent}
        disabled = {isPending}
      />
      {todo.title}
    </li>
  );
}