当react-hook-form Controller出现错误时如何聚焦?

ext*_*eck 18 reactjs yup react-hook-form

我正在使用react-hook-form渲染自定义组件Controller

\n

如果使用时出现错误register,焦点正常,但Controller不正常。

\n

当自定义组件仅处理单个输入时,我能够找到一种处理错误的方法,但当它有多个输入时,我找不到处理错误的方法。

\n

表单.js

\n
import { Controller, useForm } from "react-hook-form";\nimport CustomInput from "./CustomInput";\nimport * as yup from "yup";\n\nconst schema = yup.object({\n  name: yup.object().shape({\n    first: yup.string().required(),\n    last: yup.string().required(),\n  }),\n});\nfunction Form() {\n  const {\n    handleSubmit,\n    control,\n    formState: { errors },\n  } = useForm({\n    defaultValues: { name: { first: "", last: "" } },\n    resolver: yupResolver(schema),\n  });\n\n  const onSubmit = (data) => console.log(data);\n\n  return (\n    <form onSubmit={handleSubmit(onSubmit)}>\n      <Controller\n        name="name"\n        control={control}\n        rules={{ required: true }}\n        render={({ field }) => (\n          <CustomInput\n            value={field.value}\n            onChange={(value) => field.onChange(value)}\n            errors={errors}\n          />\n        )}\n      />\n      <button type="submit">Send</button>\n    </form>\n  );\n}\n\nexport default Form;\n
Run Code Online (Sandbox Code Playgroud)\n

自定义输入.js

\n
function CustomInput({ value, onChange, errors }) {\n  const changeFirst = (e) => {\n    onChange({ first: e.target.value, last: value?.last });\n  };\n\n  const changeLast = (e) => {\n    onChange({ first: value?.first, last: e.target.value });\n  };\n\n  return (\n    <div>\n      <input type="text" value={value.first} onChange={changeFirst} />\n      <input type="text" value={value.last} onChange={changeLast} />\n      {errors?.name && (\n        <p className="errmsg">\n          {errors?.name?.first?.message || errors?.name?.last?.message}\n        </p>\n      )}\n    </div>\n  );\n}\n\nexport default CustomInput;\n
Run Code Online (Sandbox Code Playgroud)\n

\xe2\x80\x8b当自定义组件中有多个输入时,如何获得错误焦点?

\n

Nea*_*arl 16

您可以setFocus从 RHF 使用。首先,检测errors对象何时发生变化,然后找到第一个包含该对象的字段并调用setFocus(field)

const {
  setFocus,
  formState: { errors },
  ...
} = useForm<FormValues>();

React.useEffect(() => {
  const firstError = Object.keys(errors).reduce((field, a) => {
    return !!errors[field] ? field : a;
  }, null);

  if (firstError) {
    setFocus(firstError);
  }
}, [errors, setFocus]);
Run Code Online (Sandbox Code Playgroud)

Codesandbox 演示

  • @extradeck,您使用 RHF 的方式是错误的,您永远不必自己控制表单状态,如果您使用嵌套组件,则应该使用 `useFormContext`。我在[this](https://codesandbox.io/s/69719627-how-to-focus-when-an-error-occurrs-in-react-hook-form-controller-forked-lfuqy?file中修复了你的代码=/src/App.tsx)codesandbox (2认同)

arc*_*inz 8

控制焦点的是“ref”,由于某种原因,当您使用控制器时,“ref”无法正确关联到底层元素,因此您必须使用“register”中的 ref 而不是“field”中的 ref。

import { FC } from 'react'
import { Controller, useFormContext } from 'react-hook-form'
import { TextField, TextFieldProps } from '@mui/material'

export const RhfTextField: FC<{ name: string } & TextFieldProps> = ({
  name,
  ...rest
}) => {
  const { control, register } = useFormContext()

  return (
    <Controller
      control={control}
      name={name}
      defaultValue=""
      render={({ field, fieldState: { error } }) => (
        <TextField
          {...rest}
          {...field}
          ref={register(name).ref} // overriding field.ref
          // you can do {...register(name)} and omit {...field}
          // but showing this explicitly for demonstration purposes
          error={!!error}
          helperText={error?.message ?? ''}
        />
      )}
    />
  )
}
Run Code Online (Sandbox Code Playgroud)


Chr*_*8er 7

根据@NearHuscarl 的回答,我创建了打字稿版本:

React.useEffect(() => {
    const firstError = (
      Object.keys(errors) as Array<keyof typeof errors>
    ).reduce<keyof typeof errors | null>((field, a) => {
      const fieldKey = field as keyof typeof errors;
      return !!errors[fieldKey] ? fieldKey : a;
    }, null);

    if (firstError) {
      setFocus(firstError);
    }
  }, [errors, setFocus]);
Run Code Online (Sandbox Code Playgroud)

  • 获取:类型“root”不可分配给类型“firstName” | “姓氏”| “电子邮件” | ... (4认同)

sir*_*sko 5

arcanereinz 解决方案的启发,您可以使用inputRef而不需要定义ref

我认为 MUI 中有一个错误,这种行为真的很奇怪

工作codesandbox在这里https://codesandbox.io/s/sweet-panna-ttste0?file=/src/App.tsx

import { Controller, useForm } from "react-hook-form";
import TextField from "@mui/material/TextField";

export default function App() {
  const { handleSubmit, control } = useForm();
  return (
    <form onSubmit={handleSubmit((data) => console.log(data))}>
      <Controller
        name="foo"
        control={control}
        rules={{ required: true }}
        render={({ field: { ref, ...field }, fieldState }) => (
          <TextField
            fullWidth
            variant="outlined"
            error={Boolean(fieldState.error)}
            inputRef={ref}
            {...field}
          />
        )}
      />
      <button type="submit">submit</button>
    </form>
  );
}
Run Code Online (Sandbox Code Playgroud)