Next JS中如何解决useFormStatus导致多提交按钮同时加载的问题
解决Next.js 14.2.2(App Router)多提交按钮加载状态区分问题
useFormStatus的pending状态是基于整个表单上下文的,默认情况下只要表单处于提交状态,所有按钮都会触发加载显示。要实现仅点击的按钮显示加载,需要结合客户端状态跟踪被点击的按钮,同时配合表单状态判断。
方案一:共享表单 + 客户端状态跟踪(推荐,适用于按钮共享表单字段的场景)
实现步骤
- 用
useState记录被点击的按钮标识; - 给每个按钮添加
onClick事件,点击时标记当前按钮; - 结合
useFormStatus.pending与标记的按钮,仅匹配时显示加载状态; - 表单提交完成后重置标记状态;
- 通过按钮的
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
相关产品推荐
相关产品推荐

