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

在Formik中使用useFormStatus与useFormState:提交状态异常排查

问题解决:Formik + Next.js Actions 中 useFormStatus 不生效及 TypeScript 类型处理

核心问题

  1. 用 Formik 结合 Next.js Actions 时,useFormStatus() 返回的 pending 始终为 false,但移除 Formik 改用原生表单功能正常;
  2. 尝试调用 Formik 的 setSubmitting 后,Server Action 无法接收 formData;
  3. 不清楚如何为 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 03:12:35