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

如何在Next.js Server Action中实现表单加载状态显示

在Next.js 13/14 Server Action中获取表单加载状态的方案

由于Server Component无法使用React Hooks,要实现提交按钮的加载UI,可采用以下两种实用方案:

方案一:抽离表单为客户端组件(官方推荐)

将包含表单的部分单独做成客户端组件,利用useFormStatus Hook获取加载状态。

实现步骤:

  1. 创建客户端组件SignUpForm.tsx:
'use client';

import { useFormStatus } from 'react-dom';
import { cookies, redirect } from 'next/navigation';
import { createClient } from '@/utils/supabase'; // 替换为你的Supabase创建路径

// 封装带状态的提交按钮
function SubmitButton() {
  const { pending } = useFormStatus();
  return (
    <input 
      type="submit" 
      value={pending ? '提交中...' : 'Submit'} 
      disabled={pending}
    />
  );
}

export default function SignUpForm() {
  return (
    <form action={async (formData) => {
      'use server';
      const email = formData.get("email") as string;
      const password = formData.get("password") as string;
      const cookieStore = cookies();
      const supabase = createClient(cookieStore);

      // 执行Supabase注册逻辑
      const { error } = await supabase.auth.signUp({ email, password });
      if (error) throw error;

      return redirect("/dashboard");
    }}>
      <input type="email" name="email" placeholder="Email" required />
      <input type="password" name="password" placeholder="Password" required />
      <SubmitButton />
    </form>
  );
}
  1. 在Server Component的page.tsx中引入该组件:
import SignUpForm from './SignUpForm';

export default async function Page() {
  return (
    <div>
      sign up form
      <SignUpForm />
    </div>
  );
}

说明:useFormStatus必须作为表单的子组件使用,它会自动关联父表单的Server Action执行状态,pending为true时表示Action正在处理。

方案二:用useTransition手动管理状态

若不想改动整体组件结构,可在客户端组件中用useTransition包裹Server Action调用,手动控制加载状态。

示例代码:

'use client';

import { useTransition } from 'react';
import { cookies, redirect } from 'next/navigation';
import { createClient } from '@/utils/supabase';

async function signUp(formData: FormData) {
  'use server';
  const email = formData.get("email") as string;
  const password = formData.get("password") as string;
  const cookieStore = cookies();
  const supabase = createClient(cookieStore);

  const { error } = await supabase.auth.signUp({ email, password });
  if (error) throw error;

  return redirect("/dashboard");
}

export default function SignUpForm() {
  const [isPending, startTransition] = useTransition();

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    const formData = new FormData(e.currentTarget);
    startTransition(() => {
      signUp(formData);
    });
  };

  return (
    <form onSubmit={handleSubmit}>
      <input type="email" name="email" placeholder="Email" required />
      <input type="password" name="password" placeholder="Password" required />
      <input 
        type="submit" 
        value={isPending ? '提交中...' : 'Submit'} 
        disabled={isPending}
      />
    </form>
  );
}

同样在page.tsx中引入该客户端组件即可。

注意:两种方案都需要将状态管理逻辑放在客户端组件中,这是Next.js App Router中Server Component的核心限制决定的。

内容的提问来源于stack exchange,提问作者Arrofi Reza Satria

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 11:54:51