You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NextJS 13中如何动态更新服务端渲染组件的数据?

解答

问题1:客户端组件创建分类的首选实现方式

客户端组件确实无法直接调用Prisma(Prisma是服务端依赖,不存在于客户端环境),Next.js 13 App目录推荐的Server Actions是首选方案,比单独写API路由更简洁:

  • 定义Server Action:可以单独创建一个actions文件,或者和组件放在同文件里,写一个带'use server'指令的异步函数,内部用Prisma完成创建操作:
// app/actions.ts
'use server';

import prisma from '@/lib/prisma';

export async function createCategory(name: string) {
  try {
    const newCategory = await prisma.category.create({
      data: { name },
    });
    return newCategory;
  } catch (error) {
    throw new Error('创建分类失败');
  }
}
  • 客户端组件直接调用:在提交逻辑里直接调用这个函数,无需额外写fetch请求。

如果更习惯传统API路由模式,也可以创建app/api/categories/route.ts,在POST方法内用Prisma处理创建逻辑,客户端通过fetch发送POST请求,但Server Actions能减少API路由的维护成本,更适配App目录的架构。

问题2:无刷新更新服务端渲染的分类列表

可以无缝将新分类添加到现有列表,以下是几种实用方案:

方案1:Server Actions + revalidatePath(推荐)

在Server Action完成分类创建后,调用revalidatePath让Next.js重新生成对应页面的服务端数据,客户端会自动更新界面,无需整页刷新:

// app/actions.ts
'use server';

import prisma from '@/lib/prisma';
import { revalidatePath } from 'next/cache';

export async function createCategory(name: string) {
  try {
    const newCategory = await prisma.category.create({
      data: { name },
    });
    // 重新验证展示分类列表的页面路径,比如根路径
    revalidatePath('/');
    return newCategory;
  } catch (error) {
    throw new Error('创建分类失败');
  }
}

提交后服务端会重新拉取分类数据,页面的服务端渲染部分自动更新,用户完全感知不到整页刷新。

方案2:手动更新客户端本地状态

把服务端组件获取的分类列表传递给客户端组件的状态,提交成功后直接将新分类添加到本地数组,实现即时界面更新,同时依赖revalidatePath确保服务端和客户端数据最终一致:

// app/page.tsx(服务端组件)
import prisma from '@/lib/prisma';
import CategoryForm from './category-form'; // 客户端组件

export default async function Home() {
  const categories = await prisma.category.findMany();
  return (
    <div>
      <h1>分类列表</h1>
      <ul>
        {categories.map((cat) => (
          <li key={cat.id}>{cat.name}</li>
        ))}
      </ul>
      <CategoryForm initialCategories={categories} />
    </div>
  );
}
// app/category-form.tsx(客户端组件)
'use client';

import { useState } from 'react';
import { createCategory } from './actions';

type Category = { id: string; name: string };

export default function CategoryForm({ initialCategories }: { initialCategories: Category[] }) {
  const [categories, setCategories] = useState(initialCategories);
  const [name, setName] = useState('');
  const [isModalOpen, setIsModalOpen] = useState(false);

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    try {
      const newCategory = await createCategory(name);
      // 手动添加到本地状态,即时更新界面
      setCategories([...categories, newCategory]);
      setName('');
      setIsModalOpen(false);
    } catch (error) {
      alert('创建失败');
    }
  };

  return (
    <>
      <button onClick={() => setIsModalOpen(true)}>添加分类</button>
      {isModalOpen && (
        <div className="modal">
          <form onSubmit={handleSubmit}>
            <input
              type="text"
              value={name}
              onChange={(e) => setName(e.target.value)}
              placeholder="输入分类名称"
              required
            />
            <button type="submit">提交</button>
            <button type="button" onClick={() => setIsModalOpen(false)}>取消</button>
          </form>
        </div>
      )}
    </>
  );
}

这种方式用户能看到即时的界面反馈,同时服务端数据也会同步更新,保证一致性。

方案3:使用SWR/React Query缓存更新

如果用SWR或React Query获取分类数据,提交成功后调用mutate方法触发重新获取,实现无缝更新:

// app/category-list.tsx(客户端组件,用SWR)
'use client';

import useSWR from 'swr';
import { createCategory } from './actions';

type Category = { id: string; name: string };

const fetcher = async (): Promise<Category[]> => {
  const res = await fetch('/api/categories');
  return res.json();
};

export default function CategoryList() {
  const { data: categories, mutate } = useSWR('/api/categories', fetcher);

  const handleAddCategory = async (name: string) => {
    await createCategory(name);
    // 触发重新获取数据,更新界面
    mutate();
  };

  if (!categories) return <div>加载中...</div>;

  return (
    <div>
      <ul>
        {categories.map((cat) => (
          <li key={cat.id}>{cat.name}</li>
        ))}
      </ul>
      {/* 这里可以添加触发添加分类的模态框逻辑,调用handleAddCategory */}
    </div>
  );
}

内容的提问来源于stack exchange,提问作者AnonUser456

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 02:59:54