如何在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> ); };
说明
- 获取Formik上下文:通过
useFormikContext拿到setErrors,直接操控Formik的错误状态。 - 监听action返回值:
useEffect监听props.data的变化,每次action返回新数据时触发错误映射逻辑。 - 错误码映射:根据Firebase返回的错误码,将错误信息对应到Formik的具体字段,比如
auth/user-not-found对应邮箱字段错误。 - 移除单独错误展示:原来的硬编码错误判断可以删除,现在错误会通过Formik的
ErrorMessage组件统一展示。
内容的提问来源于stack exchange,提问作者agnes
相关产品推荐
相关产品推荐

