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

如何在Formik中捕获React Router Action返回的错误并转为Formik错误?

问题:将React Router Action返回的错误映射为Formik错误

我开发了一个使用React Router actions和loaders的应用,之后把React表单替换为Formik表单,用useSubmit钩子提交数据。现在想把action函数返回的错误,通过Formik的setErrors这类辅助函数当成Formik错误展示,目前只能单独展示提交错误。

现有代码

Login组件

export const Login = () => {
  const data = useActionData<typeof action>()
  return (
    ...
    <LoginForm data={data} />
  )
}

LoginForm(片段)

const LoginForm = (props: { data: ReturnType<typeof useActionData<typeof action>> }) => {
    const submit = useSubmit()
return (
        <Formik
            initialValues={initialValues}
            validationSchema={validationSchema}
            onSubmit={(values) => submit(values, { method: "post" })}
        >
            {() => {
                return (
                    < Form
                        className='flex flex-col items-center max-w-sm gap-4 ' >
                        <div className='text-center'>

                            <Field
                                type="email"
                                name="email"
                                required
                                placeholder='Email adress'
                                className='px-3 py-1 mb-1'
                            />
                            <ErrorMessage name="email" component="div" className="text-sm text-red-500" />
                            {(typeof props.data === 'object' && props?.data?.error === 'auth/user-not-found') && <p className="text-sm text-red-500">Incorrect email</p>}
                        </div>

action.tsx

export async function action({ request }: ActionFunctionArgs) {
    const formData = await request.formData();
    const email = String(formData.get('email'));
    const password = String(formData.get('password'));

    try {
        const data = await signInWithEmailAndPassword(auth, email, password);
        localStorage.setItem('loggedUser', JSON.stringify(data.user.uid))
        return redirect('/projects')
    } catch (error) {
        if (error instanceof Error) return { error: error.code }
        return String(error)
    }
}

我尝试过两种方法但都无效:

  • 在onSubmit函数中用props结合try-catch,但提交前props.data是undefined,catch块也捕获不到错误
  • 把setErrors传入submit函数,但该函数只接受values和options作为参数

解决方案

核心思路是利用Formik的useFormikContext获取setErrors方法,结合React Router的useActionData监听action返回的错误,在组件渲染时将错误映射到Formik的错误对象中。

修改后的LoginForm代码如下:

import { useFormikContext } from 'formik';
import { useEffect } from 'react';

const LoginForm = (props: { data: ReturnType<typeof useActionData<typeof action>> }) => {
    const submit = useSubmit();
    // 获取Formik的setErrors方法
    const { setErrors } = useFormikContext();

    // 监听action返回的数据变化,映射错误到Formik
    useEffect(() => {
        if (props.data?.error) {
            const formikErrors: Record<string, string> = {};
            // 根据Firebase错误码对应到Formik字段错误
            switch (props.data.error) {
                case 'auth/user-not-found':
                    formikErrors.email = 'Incorrect email';
                    break;
                case 'auth/wrong-password':
                    formikErrors.password = 'Incorrect password';
                    break;
                case 'auth/invalid-email':
                    formikErrors.email = 'Invalid email format';
                    break;
                // 其他错误类型可以继续添加
                default:
                    formikErrors.general = 'Login failed, please try again';
            }
            setErrors(formikErrors);
        }
    }, [props.data, setErrors]);

    return (
        <Formik
            initialValues={initialValues}
            validationSchema={validationSchema}
            onSubmit={(values) => submit(values, { method: "post" })}
        >
            {() => {
                return (
                    <Form className='flex flex-col items-center max-w-sm gap-4 ' >
                        <div className='text-center'>
                            <Field
                                type="email"
                                name="email"
                                required
                                placeholder='Email address'
                                className='px-3 py-1 mb-1'
                            />
                            <ErrorMessage name="email" component="div" className="text-sm text-red-500" />
                        </div>
                        <div className='text-center'>
                            <Field
                                type="password"
                                name="password"
                                required
                                placeholder='Password'
                                className='px-3 py-1 mb-1'
                            />
                            <ErrorMessage name="password" component="div" className="text-sm text-red-500" />
                        </div>
                        {/* 展示通用错误 */}
                        <ErrorMessage name="general" component="p" className="text-sm text-red-500" />
                        <button type="submit">Login</button>
                    </Form>
                );
            }}
        </Formik>
    );
};

说明

  1. 获取Formik上下文:通过useFormikContext拿到setErrors,直接操控Formik的错误状态。
  2. 监听action返回值:useEffect监听props.data的变化,每次action返回新数据时触发错误映射逻辑。
  3. 错误码映射:根据Firebase返回的错误码,将错误信息对应到Formik的具体字段,比如auth/user-not-found对应邮箱字段错误。
  4. 移除单独错误展示:原来的硬编码错误判断可以删除,现在错误会通过Formik的ErrorMessage组件统一展示。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 14:47:35