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

Next JS中如何解决useFormStatus导致多提交按钮同时加载的问题

解决Next.js 14.2.2(App Router)多提交按钮加载状态区分问题

useFormStatus的pending状态是基于整个表单上下文的,默认情况下只要表单处于提交状态,所有按钮都会触发加载显示。要实现仅点击的按钮显示加载,需要结合客户端状态跟踪被点击的按钮,同时配合表单状态判断。

方案一:共享表单 + 客户端状态跟踪(推荐,适用于按钮共享表单字段的场景)

实现步骤

  1. 用useState记录被点击的按钮标识;
  2. 给每个按钮添加onClick事件,点击时标记当前按钮;
  3. 结合useFormStatus.pending与标记的按钮,仅匹配时显示加载状态;
  4. 表单提交完成后重置标记状态;
  5. 通过按钮的name和value,让服务器端Action区分执行不同逻辑。

代码示例

客户端组件(FormButtons.tsx)

'use client';

import { useState, useFormStatus } from 'react';
import { Form } from 'next/form';
import { formAction } from './actions';

export default function FormButtons() {
  const [loadingButton, setLoadingButton] = useState<string | null>(null);
  const formStatus = useFormStatus();

  // 表单提交完成后重置加载状态
  if (!formStatus.pending && loadingButton) {
    setLoadingButton(null);
  }

  return (
    <Form action={formAction} className="space-x-4">
      <button
        type="submit"
        name="action"
        value="button1"
        onClick={() => setLoadingButton('button1')}
        disabled={formStatus.pending && loadingButton === 'button1'}
        className="px-4 py-2 bg-blue-500 text-white rounded"
      >
        {formStatus.pending && loadingButton === 'button1' ? '加载中...' : 'Button1'}
      </button>

      <button
        type="submit"
        name="action"
        value="button2"
        onClick={() => setLoadingButton('button2')}
        disabled={formStatus.pending && loadingButton === 'button2'}
        className="px-4 py-2 bg-green-500 text-white rounded"
      >
        {formStatus.pending && loadingButton === 'button2' ? '加载中...' : 'Button2'}
      </button>
    </Form>
  );
}

服务器端Action(actions.ts)

'use server';

export async function formAction(formData: FormData) {
  const actionType = formData.get('action') as string;

  switch (actionType) {
    case 'button1':
      // 执行Button1对应的业务逻辑,这里用延迟模拟加载
      await new Promise(resolve => setTimeout(resolve, 2000));
      console.log('Button1 提交完成');
      break;
    case 'button2':
      // 执行Button2对应的业务逻辑
      await new Promise(resolve => setTimeout(resolve, 2000));
      console.log('Button2 提交完成');
      break;
    default:
      throw new Error('未知的提交操作');
  }
}

页面组件(page.tsx)

import FormButtons from './FormButtons';

export default function Home() {
  return (
    <div className="container mx-auto p-8">
      <h1 className="text-2xl font-bold mb-4">多按钮表单加载状态示例</h1>
      <FormButtons />
    </div>
  );
}

方案二:独立表单(适用于按钮无共享字段、业务逻辑完全独立的场景)

如果两个按钮不需要共享表单输入字段,可以给每个按钮单独绑定一个Form组件和独立的Action,这样每个表单的useFormStatus状态独立,点击哪个按钮就只会触发对应表单的加载状态。

代码示例

'use client';

import { useFormStatus } from 'react';
import { Form } from 'next/form';
import { button1Action, button2Action } from './actions';

// 封装独立的提交按钮组件
function SubmitButton({ action, label }: { action: () => void; label: string }) {
  const formStatus = useFormStatus();
  return (
    <Form action={action} className="inline-block mr-2">
      <button
        type="submit"
        disabled={formStatus.pending}
        className="px-4 py-2 bg-blue-500 text-white rounded"
      >
        {formStatus.pending ? '加载中...' : label}
      </button>
    </Form>
  );
}

export default function FormButtons() {
  return (
    <div>
      <SubmitButton action={button1Action} label="Button1" />
      <SubmitButton action={button2Action} label="Button2" />
    </div>
  );
}

对应的服务器端Action拆分两个独立函数即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 19:46:36