React Hook Form 中所需的属性不会在文本字段上进行验证。

huangapple go评论82阅读模式
英文:

Required property from React hook form does not validate on textfields

问题

以下是您的翻译代码部分:

  1. 我目前正在使用React-hook-form来管理我的文本字段以及验证当前的问题是required属性不起作用因为当输入为空时它不会显示小的帮助文本这里是界面的一部分示例它只显示了一些输入字段以节省不必要的JSX代码
  2. 这是我如何做的
  3. ```javascript
  4. import { FragmentType, useFragment } from '@gql/fragment-masking';
  5. import { graphql } from '@gql/gql';
  6. import { Gender } from '@gql/graphql';
  7. import {
  8. Button,
  9. Card,
  10. CardContent,
  11. Grid,
  12. MenuItem,
  13. TextField,
  14. TextFieldProps,
  15. Typography,
  16. } from '@mui/material';
  17. import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
  18. import { DesktopDatePicker } from '@mui/x-date-pickers/DesktopDatePicker';
  19. import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
  20. import dayjs, { Dayjs } from 'dayjs';
  21. import { useTranslation } from 'next-i18next';
  22. import React, { useCallback, useState } from 'react';
  23. import { useForm } from 'react-hook-form';
  24. type Inputs = {
  25. firstName: string;
  26. lastName: string;
  27. birthdate: string;
  28. street: string;
  29. postalcode: string;
  30. city: string;
  31. country: string;
  32. gender: string;
  33. email: string;
  34. leveloftrust: string;
  35. lastsignedin: string;
  36. };
  37. export const PatientInfoFragment = graphql(/* GraphQL */ `
  38. fragment PatientInfo on Patient {
  39. addresses {
  40. city
  41. lines
  42. postalCode
  43. }
  44. birthDate
  45. email
  46. gender
  47. id
  48. name {
  49. firstName
  50. lastName
  51. }
  52. status {
  53. lastSignInAt
  54. levelOfTrust
  55. }
  56. }
  57. `);
  58. interface PatientInfoProps {
  59. patient: FragmentType<typeof PatientInfoFragment>;
  60. }
  61. export function PatientInfo(props: PatientInfoProps) {
  62. // ...(其余代码)
  63. }

希望这有助于您的需求。如果有任何问题,请随时告诉我。

英文:

I'm currently using React-hook-form to manage my textfields and that includes validation.
The problem right now is that the required property does not work well, because it does not show the little helpertext under the inputfield when it is empty.
Here is there a snippet of the UI, it only shows a few of the inputfields to save you the
unnecessary JSX code
React Hook Form 中所需的属性不会在文本字段上进行验证。

This is how i have done it:

  1. import { FragmentType, useFragment } from &#39;@gql/fragment-masking&#39;;
  2. import { graphql } from &#39;@gql/gql&#39;;
  3. import { Gender } from &#39;@gql/graphql&#39;;
  4. import {
  5. Button,
  6. Card,
  7. CardContent,
  8. Grid,
  9. MenuItem,
  10. TextField,
  11. TextFieldProps,
  12. Typography,
  13. } from &#39;@mui/material&#39;;
  14. import { AdapterDayjs } from &#39;@mui/x-date-pickers/AdapterDayjs&#39;;
  15. import { DesktopDatePicker } from &#39;@mui/x-date-pickers/DesktopDatePicker&#39;;
  16. import { LocalizationProvider } from &#39;@mui/x-date-pickers/LocalizationProvider&#39;;
  17. import dayjs, { Dayjs } from &#39;dayjs&#39;;
  18. import { useTranslation } from &#39;next-i18next&#39;;
  19. import React, { useCallback, useState } from &#39;react&#39;;
  20. import { useForm } from &#39;react-hook-form&#39;;
  21. type Inputs = {
  22. firstName: string;
  23. lastName: string;
  24. birthdate: string;
  25. street: string;
  26. postalcode: string;
  27. city: string;
  28. country: string;
  29. gender: string;
  30. email: string;
  31. leveloftrust: string;
  32. lastsignedin: string;
  33. };
  34. export const PatientInfoFragment = graphql(/* GraphQL */ `
  35. fragment PatientInfo on Patient {
  36. addresses {
  37. city
  38. lines
  39. postalCode
  40. }
  41. birthDate
  42. email
  43. gender
  44. id
  45. name {
  46. firstName
  47. lastName
  48. }
  49. status {
  50. lastSignInAt
  51. levelOfTrust
  52. }
  53. }
  54. `);
  55. interface PatientInfoProps {
  56. patient: FragmentType&lt;typeof PatientInfoFragment&gt;;
  57. }
  58. export function PatientInfo(props: PatientInfoProps) {
  59. const patient = useFragment(PatientInfoFragment, props.patient);
  60. const [value, setValue] = React.useState&lt;Dayjs | null&gt;(
  61. dayjs(patient.birthDate)
  62. );
  63. const genderValueArray = Object.values(Gender);
  64. const [disabled, setDisabled] = useState(true);
  65. const { register, handleSubmit } = useForm&lt;Inputs&gt;({
  66. defaultValues: {
  67. firstName: patient.name?.firstName ?? &#39;&#39;,
  68. lastName: patient.name?.lastName ?? &#39;&#39;,
  69. birthdate: patient.birthDate ?? &#39;&#39;,
  70. country: &#39;&#39;,
  71. gender: patient.gender,
  72. email: patient.email ?? &#39;&#39;,
  73. leveloftrust: patient.status?.levelOfTrust,
  74. lastsignedin: patient.status?.lastSignInAt,
  75. postalcode: patient.addresses[0]?.postalCode ?? &#39;&#39;,
  76. city: patient.addresses[0]?.city ?? &#39;&#39;,
  77. street: patient.addresses[0]?.lines[0] ?? &#39;&#39;,
  78. },
  79. shouldUseNativeValidation: true,
  80. });
  81. const handleChange = React.useCallback(
  82. (newValue: Dayjs | null) =&gt; setValue(newValue),
  83. []
  84. );
  85. const { t } = useTranslation();
  86. const handleEditClick = useCallback(() =&gt; setDisabled(!disabled), [disabled]);
  87. const renderInputField = React.useCallback(
  88. (params: JSX.IntrinsicAttributes &amp; TextFieldProps) =&gt; {
  89. return &lt;TextField {...params} /&gt;;
  90. },
  91. []
  92. );
  93. const onSubmit = (data: any) =&gt; console.log(data);
  94. return (
  95. &lt;Card&gt;
  96. &lt;CardContent&gt;
  97. &lt;form onSubmit={handleSubmit(onSubmit)}&gt;
  98. &lt;Grid container direction=&quot;row&quot; justifyContent=&quot;space-between&quot;&gt;
  99. &lt;Grid item&gt;
  100. &lt;Typography gutterBottom variant=&quot;h5&quot; component=&quot;div&quot;&gt;
  101. {t(&#39;patient.info.title&#39;, &#39;Personal Information&#39;)}
  102. &lt;/Typography&gt;
  103. &lt;/Grid&gt;
  104. &lt;Grid item&gt;
  105. &lt;Grid container justifyContent=&quot;space-between&quot; sx={{ m: 1 }}&gt;
  106. {!disabled &amp;&amp; (
  107. &lt;Button
  108. onClick={handleEditClick}
  109. size=&quot;large&quot;
  110. variant=&quot;outlined&quot;&gt;
  111. Cancel
  112. &lt;/Button&gt;
  113. )}
  114. &lt;Button
  115. onClick={handleEditClick}
  116. size=&quot;large&quot;
  117. type=&quot;submit&quot;
  118. variant=&quot;contained&quot;&gt;
  119. {!disabled
  120. ? t(&#39;patient.setToComplete&#39;, &#39;Set to complete&#39;)
  121. : t(&#39;patient.edit&#39;, &#39;Edit&#39;)}
  122. &lt;/Button&gt;
  123. &lt;/Grid&gt;
  124. &lt;/Grid&gt;
  125. &lt;/Grid&gt;
  126. &lt;Grid container direction=&quot;row&quot;&gt;
  127. &lt;Grid
  128. container
  129. sx={{ margin: 1 }}
  130. rowSpacing={3}
  131. spacing={{ md: 4 }}&gt;
  132. &lt;Grid item&gt;
  133. &lt;TextField
  134. {...register(&#39;firstName&#39;, { required: &#39;Field needs to be filled out&#39; })}
  135. label=&quot;Name&quot;
  136. name=&quot;firstName&quot;
  137. id=&quot;component-outlined&quot;
  138. disabled={disabled}
  139. /&gt;
  140. &lt;/Grid&gt;
  141. &lt;Grid item&gt;
  142. &lt;TextField
  143. {...register(&#39;lastName&#39;, {
  144. required: &#39;Field needs to be filled out&#39;,
  145. })}
  146. label=&quot;lastname&quot;
  147. name=&quot;lastName&quot;
  148. id=&quot;component-outlined&quot;
  149. disabled={disabled}
  150. /&gt;
  151. &lt;/Grid&gt;
  152. &lt;Grid item sx={{ width: &#39;274.67px&#39; }}&gt;
  153. &lt;LocalizationProvider dateAdapter={AdapterDayjs}&gt;
  154. &lt;DesktopDatePicker
  155. {...register(&#39;birthdate&#39;, {
  156. required: &#39;Field needs to be filled out&#39;,
  157. })}
  158. label=&quot;Birthdate&quot;
  159. inputFormat=&quot;MM/DD/YYYY&quot;
  160. value={value}
  161. onChange={handleChange}
  162. renderInput={renderInputField}
  163. disabled={disabled}
  164. /&gt;
  165. &lt;/LocalizationProvider&gt;
  166. &lt;/Grid&gt;
  167. &lt;/Grid&gt;
  168. &lt;Grid
  169. container
  170. sx={{ margin: 1 }}
  171. rowSpacing={3}
  172. spacing={{ md: 4 }}&gt;
  173. &lt;Grid item&gt;
  174. &lt;TextField
  175. {...register(&#39;street&#39;)}
  176. id=&quot;component-outlined&quot;
  177. label=&quot;Street&quot;
  178. name=&quot;street&quot;
  179. disabled={disabled}
  180. /&gt;
  181. &lt;/Grid&gt;
  182. &lt;Grid item&gt;
  183. &lt;TextField
  184. {...register(&#39;postalcode&#39;)}
  185. id=&quot;component-outlined&quot;
  186. label=&quot;Postal code&quot;
  187. name=&quot;postalCode&quot;
  188. disabled={disabled}
  189. /&gt;
  190. &lt;/Grid&gt;
  191. &lt;Grid item&gt;
  192. &lt;TextField
  193. {...register(&#39;city&#39;)}
  194. id=&quot;component-outlined&quot;
  195. label=&quot;City&quot;
  196. name=&quot;city&quot;
  197. disabled={disabled}
  198. /&gt;
  199. &lt;/Grid&gt;
  200. &lt;/Grid&gt;
  201. &lt;Grid
  202. container
  203. sx={{ margin: 1 }}
  204. rowSpacing={3}
  205. spacing={{ md: 4 }}&gt;
  206. &lt;Grid item&gt;
  207. &lt;TextField
  208. {...register(&#39;country&#39;)}
  209. id=&quot;component-outlined&quot;
  210. label=&quot;Country&quot;
  211. name=&quot;country&quot;
  212. disabled={disabled}
  213. /&gt;
  214. &lt;/Grid&gt;
  215. &lt;Grid item&gt;
  216. &lt;TextField
  217. sx={{ width: &#39;242.67px&#39; }}
  218. id=&quot;outlined-select-gender&quot;
  219. select
  220. {...register(&#39;gender&#39;)}
  221. label=&quot;Gender&quot;
  222. disabled={disabled}
  223. value={patient.gender}&gt;
  224. {genderValueArray.map(option =&gt; (
  225. &lt;MenuItem key={option} value={option}&gt;
  226. {option}
  227. &lt;/MenuItem&gt;
  228. ))}
  229. &lt;/TextField&gt;
  230. &lt;/Grid&gt;
  231. &lt;Grid item&gt;
  232. &lt;TextField
  233. {...register(&#39;email&#39;)}
  234. id=&quot;component-outlined&quot;
  235. label=&quot;Email&quot;
  236. name=&quot;email&quot;
  237. disabled
  238. /&gt;
  239. &lt;/Grid&gt;
  240. &lt;/Grid&gt;
  241. &lt;Grid
  242. container
  243. sx={{ margin: 1 }}
  244. rowSpacing={3}
  245. spacing={{ md: 4 }}&gt;
  246. &lt;Grid item&gt;
  247. &lt;TextField
  248. {...register(&#39;leveloftrust&#39;)}
  249. id=&quot;component-outlined&quot;
  250. label=&quot;Level of trust&quot;
  251. name=&quot;leveloftrust&quot;
  252. disabled
  253. /&gt;
  254. &lt;/Grid&gt;
  255. &lt;Grid item&gt;
  256. &lt;TextField
  257. {...register(&#39;lastsignedin&#39;)}
  258. id=&quot;component-outlined&quot;
  259. label=&quot;Last signed in&quot;
  260. name=&quot;lastsignedin&quot;
  261. disabled
  262. /&gt;
  263. &lt;/Grid&gt;
  264. &lt;/Grid&gt;
  265. &lt;/Grid&gt;
  266. &lt;/form&gt;
  267. &lt;/CardContent&gt;
  268. &lt;/Card&gt;
  269. );
  270. }

答案1

得分: 0

If you're using React Hook Form with MUI Textfield, you'll need to use the Controller component wrapper from react-hook-form for your MUI Textfield components.

View Integrating with UI Libraries

For example, you would grab the Input component from MUI's Core library instead:

  1. import Input from "@material-ui/core/Input";

Then in your form, you'd wrap it using Controller like:

  1. <Controller
  2. name="firstName"
  3. control={control}
  4. render={({ field }) => <Input {...field} />}
  5. />

Then MUI Input has error (boolean) and helperText (string) as available properties on the Input component. For example using Formik (but any form library would work with MUI):

  1. <TextField
  2. id="password"
  3. name="password"
  4. label="Password"
  5. type="password"
  6. value={formik.values.password}
  7. onChange={formik.handleChange}
  8. error={!!formik.errors.password && formik.touched.password}
  9. helperText={formik.touched.password && formik.errors.password}
  10. required
  11. />

Otherwise, reviewing their documentation on how to Handle Errors, it appears with no library they have you handle error output manually using the errors object from formState. You'd destructure that data like:

  1. const { register, formState: { errors }, handleSubmit } = useForm();

Finally, they link a very handy CodeSandbox which uses a @hookform/error-message library that can wrap regular HTML inputs. If you want to stick with MUI, I'd go with the helperText and error properties.

英文:

If you're using React Hook Form with MUI Textfield, you'll need to use the Controller component wrapper from react-hook-form for your MUI Textfield components.

View Integrating with UI Libraries

For example, you would grab the Input component from MUI's Core library instead:

  1. import Input from &quot;@material-ui/core/Input&quot;;

Then in your form you'd wrap it using Controller like:

  1. &lt;Controller
  2. name=&quot;firstName&quot;
  3. control={control}
  4. render={({ field }) =&gt; &lt;Input {...field} /&gt;}
  5. /&gt;

Then MUI Input has has error boolean and helperText string as available properties on the Input component. For example using Formik (but any form library would work with MUI):

  1. &lt;TextField
  2. id=&quot;password&quot;
  3. name=&quot;password&quot;
  4. label=&quot;Password&quot;
  5. type=&quot;password&quot;
  6. value={formik.values.password}
  7. onChange={formik.handleChange}
  8. error={!!formik.errors.password &amp;&amp; formik.touched.password}
  9. helperText={formik.touched.password &amp;&amp; formik.errors.password}
  10. required
  11. /&gt;

Otherwise, reviewing their documentation on how to Handle Errors, it appears with no library they have you handle error output manually using the errors object from formState. You'd destructure that data like:

  1. const { register, formState: { errors }, handleSubmit } = useForm();

Finally, they link a very handy CodeSandbox which uses a @hookform/error-message library that can wrap regular HTML inputs. If you want to stick with MUI, I'd go with the helperText and error properties.

huangapple
  • 本文由 发表于 2023年2月10日 04:14:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/75403965.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定