简体   繁体   中英

react-hook-form is leaving the input field while typing with mode property set

I am using react-hook-form V7 with Material UI to create a simple form however, there is a weird behavior when setting the mode in useForm({mode: ".."}) Here is my code snippet:

import { yupResolver } from "@hookform/resolvers/yup";
import { Button, Grid, Link, TextField, Typography } from "@material-ui/core";
import { makeStyles } from "@material-ui/core/styles";
import Alert from "@material-ui/lab/Alert";
import { Controller, useForm } from "react-hook-form";
import { _MESSAGES } from "src/constants/messages";
import * as yup from "yup";
import AuthContainer from "./AuthContainer";

// #####################   Helpers    ######################
/**
 * @param {object} error
 * @param {string} msg
 */
const renderError = (error, msg) =>
  error ? <Alert severity="error">{msg}</Alert> : "";

// ##############   Schema & Default Values   ##############
const schema = yup.object().shape({
  email: yup.string().required(),
  password: yup.string().required(),
});

const defaultValues = {
  email: "",
  password: "",
};

// ######################   Styles    ######################
const useStyles = makeStyles((theme) => ({
  form: {
    width: "100%", // Fix IE 11 issue.
    marginTop: theme.spacing(1),
  },
  submit: {
    margin: theme.spacing(3, 0, 2),
  },
}));

// #################   Main Component    ###################
const SignInPage = () => {
  const classes = useStyles();
  const {
    handleSubmit,
    formState: { errors },
    reset,
    control,
  } = useForm({
    mode: "all",
    resolver: yupResolver(schema),
    defaultValues,
  });

  const onSubmit = (data) => {
    console.log(data);

    // Reset form fields
    reset();
  };

  console.log(errors);

  const Form = () => (
    <>
      <Typography component="h1" variant="h5">
        Sign In
      </Typography>
      <form className={classes.form} onSubmit={handleSubmit(onSubmit)}>
        <Controller
          control={control}
          name="email"
          render={({ field }) => (
            <TextField
              variant="outlined"
              margin="normal"
              fullWidth
              autoComplete="email"
              label="Email Address"
              {...field}
              error={errors.email?.message ? true : false}
            />
          )}
        />
        {renderError(errors.email?.message, _MESSAGES.required)}
        <Controller
          control={control}
          defaultValue=""
          name="password"
          render={({ field }) => (
            <TextField
              variant="outlined"
              margin="normal"
              fullWidth
              autoComplete="current-password"
              type="password"
              label="Password"
              {...field}
              error={errors.password?.message ? true : false}
            />
          )}
        />
        {renderError(errors.password?.message, _MESSAGES.required)}
        <Button
          type="submit"
          fullWidth
          variant="contained"
          color="primary"
          className={classes.submit}
        >
          Sign In
        </Button>
        <Grid container>
          <Grid item xs={12}>
            <Link href="#" variant="body2">
              Forgot password?
            </Link>
          </Grid>
          <Grid item>
            <Link href="#" variant="body2" color="secondary">
              {"Don't have an account? Sign Up"}
            </Link>
          </Grid>
        </Grid>
      </form>
    </>
  );

  return <AuthContainer Form={<Form />} />;
};

export default SignInPage;

What might have gone wrong?

Here is a Gif of what happened:

  1. selected the input field
  2. started typing
  3. after one char it leaves the input field so, I need to select it to type again.

在此处输入图像描述

Move your Form component to its own Component, otherwise, you are mount and unmount your Form Component each re-render.

const App = () => {
  const Form = () => {} // mount and unmount with each re-render
  return <div><Form /></div>
}

to

const Form = () => {}

const App = () => {
  return <div><Form /></div>
}

You have to pass down the ref . For MUI TextField should do the following:

<Controller
  control={methods.control}
  name="fieldName"
  render={({ field: { ref, ...field } }) => (
    <TextField {...field} inputRef={ref} /> // ⬅️ The ref
  )}
/>

A working demo: https://codesandbox.io/s/broken-microservice-xpuru

Does it work now?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM