javascriptreactjsformsreact-hook-form

React hook form field resets after submission


The component uses react-hook-form for a two-step form (email then password). After submitting the email address in the first step, the email value appears to be reset to undefined. This prevents the submission of the final form since the email is no longer available.

export default function App() {
  const [inProgress, setInProgress] = useState(false);
  const handleSubmit = (email: string, password: string) => {
    console.log("SimplifiedSignInForTesting submitted:", { email, password });
    setInProgress(true);
    setTimeout(() => {
      setInProgress(false);
    }, 1500);
  };

  return <SignIn submit={handleSubmit} inProgress={inProgress} />;
}

interface SignInProps {
  submit: (email: string, password: string) => void;
  inProgress: boolean;
}

export const SignIn = ({ submit, inProgress }: SignInProps) => {
  const [isEmailSubmitted, setIsEmailSubmitted] = useState(false);

  const schema = z.object({
    email: z.string(),
    password: z.string(),
  });

  type SignInFormData = z.infer<typeof schema>;

  const {
    register,
    handleSubmit,
    setFocus,
    watch,
    formState: { errors, isSubmitting },
  } = useForm<SignInFormData>({
    resolver: zodResolver(schema),
    shouldUnregister: false,
    defaultValues: {
      email: "",
      password: "",
    },
  });

  const onSubmitInternal = useCallback(
    async (data: SignInFormData) => {
      const { email, password } = data;
      console.log(email, password);

      if (!isEmailSubmitted) {
        setIsEmailSubmitted(true);
        setTimeout(() => setFocus("password"), 0);
        return;
      }

      submit(email, password);
    },
    [isEmailSubmitted, setIsEmailSubmitted, submit, setFocus]
  );

  const isLoading = inProgress || isSubmitting;
  const emailErrorMessage = errors.email?.message;

  const email = watch("email");

  React.useEffect(() => {
    console.log(email);
  }, [email]);

  return (
    <form onSubmit={handleSubmit(onSubmitInternal)} noValidate>
      <input
        tabIndex={0}
        autoFocus
        aria-label="email"
        type="email"
        disabled={isLoading || isEmailSubmitted}
        {...register("email")}
      />
      {emailErrorMessage && <span>{emailErrorMessage}</span>}
      <input
        aria-label="password"
        type="password"
        autoComplete="current-password"
        style={{ display: isEmailSubmitted ? "initial" : "none" }}
        disabled={isLoading}
        tabIndex={isEmailSubmitted ? 0 : -1}
        {...register("password")}
      />
      <input
        tabIndex={0}
        type="submit"
        aria-label={isEmailSubmitted ? "login" : "continue"}
      />
    </form>
  );
};

https://codesandbox.io/p/sandbox/affectionate-tu-6fk26l

The form submission should have both the email and password


Solution

  • According to handleSubmit documentation:

    disabled inputs will appear as undefined values in form values. If you want to prevent users from updating an input and wish to retain the form value, you can use readOnly or disable the entire . Here is an example.

    In your Form, when a user first clicks to submit the e-mail, you are disabling the field, which in turn makes the email prop undefined.

    To fix this behaviour, you should remove the isEmailSubmitted from the disabled prop, and add a new prop, readOnly with isEmailSubmitted as value

    Here you have a working example, that I forked from your codesandbox if you want to see it working