在Formik中使用useFormStatus与useFormState:提交状态异常排查
问题解决:Formik + Next.js Actions 中 useFormStatus 不生效及 TypeScript 类型处理
核心问题
- 用 Formik 结合 Next.js Actions 时,
useFormStatus()返回的pending始终为false,但移除 Formik 改用原生表单功能正常; - 尝试调用 Formik 的
setSubmitting后,Server Action 无法接收formData; - 不清楚如何为 Server Action 配置 TypeScript 类型。
原因分析
useFormStatus依赖原生<form>元素的action属性绑定 Server Action,且组件必须是该表单的直接后代。Formik 的<Form>组件默认通过自身onSubmit处理提交逻辑,未设置action属性,导致 React 无法跟踪提交状态。- 直接调用
setSubmitting会干扰 Formik 内部提交流程,导致表单数据未被正确封装为FormData传递给 Server Action。
解决方案
1. 替代 useFormStatus:使用 Formik 内置状态
无需依赖 useFormStatus,直接利用 Formik 提供的 isSubmitting 状态判断提交状态,这是适配 Formik 流程的最优方案:
修改 SubmitButton 组件,通过 useFormikContext 获取 Formik 内部状态:
"use client"; import { useFormikContext } from "formik"; import FormButton from "@/FormButton"; export const SubmitButton = () => { const { isSubmitting } = useFormikContext(); return ( <FormButton title={isSubmitting ? "Searching..." : "Search"} disabled={isSubmitting} /> ); };
2. 让 Formik 适配 Next.js Actions 的 useFormStatus
若必须使用 useFormStatus,需为 Formik 的 <Form> 绑定 Server Action 的 action 属性,同时调整 onSubmit 逻辑确保 formData 正确传递:
修改客户端代码中的 Formik 配置:
"use client"; import { useFormState } from "react-dom"; import { FieldControl } from "@/FieldControl"; import FormButton from "@/FormButton"; import { Formik, Form } from "formik"; import * as Yup from "yup"; import { action } from "@/src/actions"; // 定义表单值类型 type FormValues = { field1: string; field2: string; field3: string; }; const LaunchPage = (props: { initialValuesPageData: any }) => { const { initialValuesPageData } = props; const initialValues: FormValues = { field1: "", field2: "", field3: "", }; const validationSchema = Yup.object().shape({ field1: Yup.string(), field2: Yup.string(), field3: Yup.string(), }); const [data, formAction] = useFormState(action, initialValuesPageData); return ( <> <Formik initialValues={initialValues} validationSchema={validationSchema} validateOnBlur={true} // 调整 onSubmit:将 Formik 值转为 FormData 后调用 Server Action onSubmit={async (values, { setSubmitting }) => { const formData = new FormData(); Object.entries(values).forEach(([key, value]) => { formData.append(key, value); }); await formAction(formData); setSubmitting(false); }} > {({ handleBlur }) => ( {/* 为 Form 设置 action 属性,绑定 Server Action */} <Form action={formAction}> <FieldControl control="text" label="Field1" name="field1" onBlur={handleBlur} /> <FieldControl control="text" label="Field2" name="field2" onBlur={handleBlur} /> <FieldControl control="text" label="Field3" name="field3" onBlur={handleBlur} /> <SubmitButton /> </Form> )} </Formik> {data && <Table data={data} />} </> ); }; export default LaunchPage;
3. Server Action 的 TypeScript 类型配置
为 Server Action 定义明确类型,替换 any:
'use server' // 定义表单数据类型 type FormDataPayload = { field1: string; field2: string; field3: string; }; // 定义返回数据类型(根据你的 fetchAPI 返回结构调整) type ActionResponse = { // 你的返回数据字段示例 id?: number; result?: string; } | null; export const action = async(currentState: ActionResponse, formData: FormData) => { // 从 FormData 解析并转为类型化对象 const payload: FormDataPayload = { field1: formData.get('field1') as string, field2: formData.get('field2') as string, field3: formData.get('field3') as string, }; const data = await fetchAPI(payload); return data as ActionResponse; }
关键说明
- 方案一(使用 Formik 内置状态)更简洁,完全适配 Formik 提交流程,推荐优先使用;
- 方案二(适配 useFormStatus)需手动处理 FormData 转换,适合必须依赖 React 原生表单状态的场景;
- TypeScript 类型定义需与实际表单字段、API 返回结构保持一致,减少不必要的类型断言。
内容的提问来源于stack exchange,提问作者Bhoomi Pipalia
相关产品推荐
相关产品推荐

