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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 03:54:57