React Router v6 Action执行后如何重置表单至初始值?
解决方案:声明式实现表单重置
方案1:利用useActionData传递重置信号
通过Action返回重置标识,组件监听该标识触发Formik重置,无需命令式操作。
1. 修改Profile Action
移除redirect,返回包含重置指令的对象:
export const action = (queryClient, dispatch) => async ({ request, params }) => { const formData = await request.formData(); const actionType = formData.get('intent'); switch (actionType) { case SUBMIT.EDIT_PROFILE: { const userData = Object.fromEntries(formData); try { await UserService.editUser(userData); queryClient.invalidateQueries({ queryKey: [QUERY_KEY.USER] }); // 返回重置标识 return { resetForm: true }; } catch (err) { console.log('Edit user error:', err); return { error: err.message }; } } default: { throw new Response('', { status: 404, statusText: 'Submit action not found' }); } } };
2. 修改Profile组件
添加useActionData监听Action结果,调用Formik的resetForm方法:
import { useActionData, useNavigation } from 'react-router-dom'; import { useEffect } from 'react'; export const Profile = () => { const navigation = useNavigation(); const actionData = useActionData(); // 获取Action返回的数据 const isSubmitting = navigation.state === 'submitting' || navigation.state === 'loading'; const { queryKey, queryFn } = userQuery(); const { data: user } = useQuery({ queryKey, queryFn, }); const { name, email } = user || { name: '', email: '' }; const initialValues = { name, email, password: '', }; const validationSchema = Yup.object({ // 保持原有校验规则 name: Yup.string() .min(2, intl.formatMessage({ id: 'form.errors.name.min' })) .required(intl.formatMessage({ id: 'form.errors.input.required' })), email: Yup.string() .email(intl.formatMessage({ id: 'form.errors.email.incorrect' })) .required(intl.formatMessage({ id: 'form.errors.input.required' })), password: Yup.string() .min(3, intl.formatMessage({ id: 'form.errors.password.min' })) .required(intl.formatMessage({ id: 'form.errors.input.required' })), }); return ( <Formik initialValues={initialValues} validationSchema={validationSchema} enableReinitialize // 当用户数据更新时自动同步初始值 > {({ errors, isValid, touched, dirty, resetForm }) => { // 监听Action返回的重置信号 useEffect(() => { if (actionData?.resetForm) { resetForm(); // 重置表单到初始值,dirty状态变为false } }, [actionData, resetForm]); return ( <Form method='PATCH' className={clsx(s.form)}> {/* 保持原有表单字段 */} <Field name={'name'} type={'text'} as={InputElement} error={touched.name && !!errors.name} errorText={touched.name && errors.name} /> <Field name={'email'} type={'email'} as={InputElement} error={touched.email && !!errors.email} errorText={touched.email && errors.email} /> <Field name={'password'} type={'password'} as={InputElement} error={touched.password && !!errors.password} errorText={touched.password && errors.password} /> {/* 保持原有按钮逻辑 */} {dirty && ( <> <FormSubmitBtn value={SUBMIT.EDIT_PROFILE} disabled={!isValid || isSubmitting} extraClass={clsx(s.input_submit)}> {isSubmitting ? ( <ButtonLoader /> ) : ( intl.formatMessage({ id: 'profile.form.submit' }) )} </FormSubmitBtn> <Button type='secondary' htmlType='reset' extraClass={clsx(s.input_cancel)}> {intl.formatMessage({ id: 'profile.form.cancel' })} </Button> </> )} </Form> ); }} </Formik> ); };
方案2:保留redirect时的状态传递
如果需要保留页面跳转逻辑,可以通过redirect的state传递重置指令:
1. 修改Action的redirect逻辑
return redirect({ pathname: PATH.PROFILE, state: { resetForm: true }, });
2. 修改Profile组件监听路由状态
import { useLocation, useNavigate } from 'react-router-dom'; // 在Formik的render函数内添加: const location = useLocation(); const navigate = useNavigate(); useEffect(() => { if (location.state?.resetForm) { resetForm(); // 清除路由状态,避免刷新页面后重复触发 navigate(PATH.PROFILE, { replace: true, state: {} }); } }, [location.state, resetForm, navigate]);
关键说明
resetForm():Formik内置方法,会将表单值重置为initialValues,同时重置dirty、touched等状态为初始值(dirty变为false)enableReinitialize:确保当React Query获取到最新用户数据时,Formik的初始值自动同步,避免表单显示旧数据- 声明式风格:通过React Router的状态传递和Formik的内置方法实现,无需手动操作DOM或状态
内容的提问来源于stack exchange,提问作者Arty Gvozdenkov
相关产品推荐
相关产品推荐

