首页 > 解决方案 > React Hook Form - Typescript 问题和错误处理?

问题描述

我目前正在从 Formik 切换到 React Hook Form。我也在使用 Material UI V5 和是的进行验证。

我目前有两个问题要解决:

  1. 各种 RHF 道具和选项会弹出 TS 错误。
  2. 我希望能够同时验证 onBlur 和 onSubmit,但 HTML5 验证当前显示 onSubmit。

这是我的代码:

import { useForm, Controller } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import {
  Box,
  Button,
  CircularProgress,
  Grid,
  InputAdornment,
  TextField,
  Theme,
  Typography,
  useMediaQuery,
} from '@mui/material';
import { validationSchema } from './validationSchema'

const LoginForm: React.FC = () => {
  const {
    control,
    handleSubmit,
    formState: { errors },
  } = useForm({
    resolver: yupResolver(validationSchema),
    mode: 'onBlur',
    reValidateMode: 'onChange',
    defaultValues: { // <-- TS Error: Type '{ email: string; password: string; }' is not assignable to type '{ [x: string]: undefined; email?: string | undefined; password?: string | undefined; }'.
      email: '',
      password: '',
    },
  });

  const handleSignin = async (credentials: Values) => {
    const { error } = await signIn(Provider.Email, credentials);

    if (error) {
      setLoadingLogin(false);
      setLoginError(true);
    } else {
      router.push('/');
    }
  };

  return (
    <BoxContainer>
      <form onSubmit={handleSubmit(handleSignin)}>
        <Grid container spacing={2}>
          <Grid item xs={12}>
            <Controller
              name="email" // <-- TS Error: Type 'string' is not assignable to type 'never'.
              control={control}
              render={({ field }) => (
                <TextField
                  {...field}
                  error={!!errors?.email}
                  helperText={errors?.email?.message} // <-- TS Error: Property 'message' does not exist on type 'never'.
                  size="small"
                  autoFocus
                  label="Email"
                  fullWidth
                  autoComplete="email"
                  type="email"
                  variant="outlined"
                  InputProps={{
                    startAdornment: (
                      <InputAdornment position="start">
                        <EmailIcon />
                      </InputAdornment>
                    ),
                  }}
                />
              )}
            />
          </Grid>
          <Grid item xs={12}>
            <Controller
              name="password" // <-- TS Error: Type 'string' is not assignable to type 'never'.
              error={!!errors?.password}
              helperText={errors?.password?.message} // <-- TS Error: Property 'message' does not exist on type 'never'.
              control={control}
              render={({ field }) => (
                <TextField
                  {...field}
                  size="small"
                  autoComplete="password"
                  label="Password"
                  fullWidth
                  type="password"
                  variant="outlined"
                  InputProps={{
                    startAdornment: (
                      <InputAdornment position="start">
                        <LockIcon />
                      </InputAdornment>
                    ),
                  }}
                />
              )}
            />
          </Grid>
          {loginError && (
            <Grid item xs={12}>
              <Alert severity="error">
                There was an error logging in.
                <br /> Please try again.
              </Alert>
            </Grid>
          )}
          <Grid item xs={12}>
            <Button
              color="primary"
              // disabled={isSubmitting || loadingLogin}
              disableElevation
              disableRipple
              fullWidth
              size="large"
              type="submit"
              variant="contained">
              Signin
            </Button>
          </Grid>
          <Grid item xs={12}>
            <Typography
              variant="body2"
              color="textSecondary"
              className="text-center">
              Don't have an account?{' '}
              <Link href="/register">
                <a>Signup</a>
              </Link>
            </Typography>
            {!isDesktop && (
              <Box marginTop={2}>
                <Typography
                  variant="body1"
                  color="textSecondary"
                  className="text-center">
                  <Link href="/forgot-password">
                    <a>Forgot your password?</a>
                  </Link>
                </Typography>
              </Box>
            )}
            <Box mt={2} className="text-center">
              <Typography
                variant="subtitle2"
                color="textSecondary"
                className="mt-4">
                By continuing to use GearCloset, you agree to our{' '}
                <Link href="/terms-and-conditions">
                  <a>Terms and Conditions.</a>
                </Link>
              </Typography>
            </Box>
          </Grid>
        </Grid>
      </form>
    </BoxContainer>
  );
}

我已经在代码返回 TS 错误旁边发表了评论。

第二个问题是,当我输入无效或空白电子邮件,然后按 Tab 时,输入的错误状态正常工作,但是当我按下提交按钮时,电子邮件输入显示正常的 HTML5 验证警告。我希望该模式以某种方式同时成为 onBlur 和 onSubmit。

在这一点上,似乎 RHF工作,我只是清理错误和功能。

标签: typescriptmaterial-uireact-hook-form

解决方案


我认为您的第一个问题可能与这个开放的 Github问题有关。有一个解决方法,检查这个 SO answer

对于第二个,您必须noValidate在元素上设置属性<form />并让 RHF 完全处理验证。

<form onSubmit={handleSubmit(handleSignin)} noValidate>

推荐阅读