diff --git a/src/components/ApiGallery.tsx b/src/components/ApiGallery.tsx index 9c54629e8..acee21e83 100644 --- a/src/components/ApiGallery.tsx +++ b/src/components/ApiGallery.tsx @@ -51,11 +51,11 @@ export default function ApiGallery() { const name = window.location.hash.toLowerCase().slice(1) if (name === "controller") { - router.push(`/docs/usecontroller/${name}`) + router.replace(`/docs/usecontroller/${name}`) } else if (USEFORM_HASHES.includes(name)) { - router.push(`/docs/useform/${name}`) + router.replace(`/docs/useform/${name}`) } else if (["useformcontext", "usefieldarray"].includes(name)) { - router.push(`/docs/${name}`) + router.replace(`/docs/${name}`) } }, [router]) diff --git a/src/components/HomePage.tsx b/src/components/HomePage.tsx index c5ea455fc..4a76433a1 100755 --- a/src/components/HomePage.tsx +++ b/src/components/HomePage.tsx @@ -112,178 +112,180 @@ function HomePage({ monthlyDownloads = 0 }: { monthlyDownloads?: number }) { )} - +
+ - { - setFeaturePlay(true) - }} - > - - - - - { - setCodeComparePlay(true) - }} - > - - - { - setIsolatePlay(true) - }} - > - - - { - setWatchPlay(true) - }} - > - - + { + setFeaturePlay(true) + }} + > + + + - { - setRenderPlay(true) - }} - > - - + { + setCodeComparePlay(true) + }} + > + + + { + setIsolatePlay(true) + }} + > + + + { + setWatchPlay(true) + }} + > + + - {monthlyDownloads > 0 && ( { - setCounterPlay(true) + setRenderPlay(true) }} > - + + + {monthlyDownloads > 0 && ( + { + setCounterPlay(true) + }} > -
-
- + +
+
+ +
+

+ Monthly{" "} + + npm downloads + +

-

- Monthly{" "} + + + )} + + +

+
+ +

Javascript Rising Star

+

+ React Hook Form have matured and evolved around hooks. + Simplifies a piece of React development. +

+
+
+ +

React Open Source Awards

+

+ The winner of 2020 GitNation React OS Award for the category of + Productivity Booster. +

+
+
+ +

Technology Radar

+

+ Featured in the{" "} - npm downloads - + Thoughtworks Technology Radar + {" "} + under Languages & Frameworks, adopted since 2025.

-
-
- - )} - - -
-
- -

Javascript Rising Star

-

- React Hook Form have matured and evolved around hooks. Simplifies - a piece of React development. -

-
-
- -

React Open Source Awards

-

- The winner of 2020 GitNation React OS Award for the category of - Productivity Booster. -

-
- -
-
+ +
+ - -
- {TESTIMONIALS.map((quote) => ( -
- -
{quote}
-
- ))} -
-
+ +
+ {TESTIMONIALS.map((quote) => ( +
+ +
{quote}
+
+ ))} +
+
- - - + + + -
+
-
+ -
-

{home.findInteresting.heading}

-

{home.findInteresting.description}

-
- - {home.getStarted} - - - API Documentation - -
-
+
+

{home.findInteresting.heading}

+

{home.findInteresting.description}

+
+ + {home.getStarted} + + + API Documentation + +
+
+
diff --git a/src/components/Watcher.tsx b/src/components/Watcher.tsx index f4026b1cd..5f0c02221 100644 --- a/src/components/Watcher.tsx +++ b/src/components/Watcher.tsx @@ -47,6 +47,7 @@ const WatchGroup = ({
{ setChecked(!check) diff --git a/src/components/mdx/youtube.tsx b/src/components/mdx/youtube.tsx index 44bbfd651..e5c0ebda2 100644 --- a/src/components/mdx/youtube.tsx +++ b/src/components/mdx/youtube.tsx @@ -30,6 +30,7 @@ export const YouTube: FunctionComponent = ( data-testid="youtube" title={`youTube-${youTubeId}`} src={src} + loading="lazy" frameBorder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowFullScreen diff --git a/src/components/seo.tsx b/src/components/seo.tsx index 1d4fc1b51..357c1a92a 100644 --- a/src/components/seo.tsx +++ b/src/components/seo.tsx @@ -31,10 +31,12 @@ function SEO({ title, description, jsonLd = [], + noindex = false, }: { title: string description?: string jsonLd?: object[] + noindex?: boolean }) { const router = useRouter() const metaTitle = title ? `${title} | ${SITE_NAME}` : DEFAULT_TITLE @@ -46,7 +48,11 @@ function SEO({ {metaTitle} - + {noindex ? ( + + ) : ( + + )} diff --git a/src/content/docs/usecontroller.mdx b/src/content/docs/usecontroller.mdx index 482084f04..a1ea883ec 100644 --- a/src/content/docs/usecontroller.mdx +++ b/src/content/docs/usecontroller.mdx @@ -51,6 +51,7 @@ The following table contains information about properties which `useController` | `fieldState` | `invalid` | boolean | Invalid state for current input. | | `fieldState` | `isTouched` | boolean | Touched state for current controlled input. | | `fieldState` | `isDirty` | boolean | Dirty state for current controlled input. | +| `fieldState` | `isValidating` | boolean | Validating state for current controlled input. | | `fieldState` | `error` | object | error for this specific input. | | `formState` | `isDirty` | boolean | Set to `true` after the user modifies any of the inputs. **Important:** Make sure to provide all inputs' `defaultValues` at `useForm`, so hook form can have a single source of truth to compare whether the form is dirty. | | `formState` | `dirtyFields` | object | An object with the user-modified fields. Make sure to provide all inputs' `defaultValues` via `useForm`, so the library can compare against the `defaultValues`. | diff --git a/src/content/docs/usecontroller/controller.mdx b/src/content/docs/usecontroller/controller.mdx index d2624237f..a060b6e2b 100644 --- a/src/content/docs/usecontroller/controller.mdx +++ b/src/content/docs/usecontroller/controller.mdx @@ -21,7 +21,7 @@ The following table contains information about the arguments for `Controller`. | ------------------ | ------------------------------------------------------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `name` | [`FieldPath`](/ts#FieldPath "FieldPath type") | ✓ | Unique name of your input. | | `control` | [`Control`](/ts#Control "Control type") | | The [`control`](/docs/useform/control) object is from invoking `useForm`. Optional when using `FormProvider`. | -| `render` | Function | | This is a [render prop](https://legacy.reactjs.org/docs/render-props.html). A function that returns a React element and provides the ability to attach events and value into the component. This simplifies integrating with external controlled components with non-standard prop names. Provides `field` (with `onChange`, `onBlur`, `name`, `ref`, `value`), `fieldState`, and `formState` objects to the render callback. | +| `render` | Function | ✓ | This is a [render prop](https://legacy.reactjs.org/docs/render-props.html). A function that returns a React element and provides the ability to attach events and value into the component. This simplifies integrating with external controlled components with non-standard prop names. Provides `field` (with `onChange`, `onBlur`, `name`, `ref`, `value`), `fieldState`, and `formState` objects to the render callback. | | `rules` | Object | | Validation rules in the same format for [`register` options](/docs/useform/register#options), which includes:

required, min, max, minLength, maxLength, pattern, validate | | `shouldUnregister` | boolean = false | | Input will be unregistered after unmount and defaultValues will be removed as well.

**Note:** this prop should be avoided when using with `useFieldArray` as `unregister` function gets called after input unmount/remount and reorder. | | `disabled` | boolean = false | | `disabled` prop will be returned from `field` prop. Controlled input will be disabled and its value will be omitted from the submission data. | @@ -45,6 +45,7 @@ The following table contains information about properties which `Controller` pro | `fieldState` | `invalid` | boolean | Invalid state for current input. | | `fieldState` | `isTouched` | boolean | Touched state for current controlled input. | | `fieldState` | `isDirty` | boolean | Dirty state for current controlled input. | +| `fieldState` | `isValidating` | boolean | Validating state for current controlled input. | | `fieldState` | `error` | object | error for this specific input. | | [`formState`](/docs/useform/formstate) | `isDirty` | boolean | Set to `true` after the user modifies any of the inputs.
  1. **Important:** Make sure to provide all inputs' defaultValues at the useForm level, so the library can have a single source of truth to compare whether the form is dirty.
  2. File-type inputs will need to be managed at the app level due to the ability to cancel file selection and [FileList](https://developer.mozilla.org/en-US/docs/Web/API/FileList) object.
| | `formState` | `dirtyFields` | object | An object with the user-modified fields. Make sure to provide all inputs' defaultValues via useForm, so the library can compare against the `defaultValues`
  1. **Important:** Make sure to provide defaultValues at the useForm level, so the library can have a single source of truth to compare each field's dirtiness.
  2. Dirty fields will **not** represent as `isDirty` formState, because dirty fields are marked field dirty at the field level rather than the entire form. If you want to determine the entire form state use `isDirty` instead.
| diff --git a/src/content/docs/usefieldarray.mdx b/src/content/docs/usefieldarray.mdx index 3f76b6f0e..3e65d65a8 100644 --- a/src/content/docs/usefieldarray.mdx +++ b/src/content/docs/usefieldarray.mdx @@ -18,7 +18,7 @@ sidebar: apiLinks Custom hook for working with Field Arrays (dynamic forms). The motivation is to provide a better user experience and performance. You can watch [this short video](https://www.youtube.com/watch?v=Q7lrHuUfgIs) to visualize the performance enhancement. -### Props +### Props {#props} --- @@ -28,7 +28,7 @@ Custom hook for working with Field Arrays (dynamic forms). The motivation is to | `control` | Object | | [`control`](/docs/useform/control) object provided by `useForm`. It's optional if you are using `FormProvider`. | | `shouldUnregister` | boolean | | Whether the Field Array will be unregistered after unmounting. | | `disabled` | boolean | | Disables the entire field array. When `true`, `fields` remains populated but every entry's `disabled` property is set to `true` (see [Return](#return)), all mutation methods (`append`, `prepend`, `insert`, `remove`, `swap`, `move`, `update`, `replace`) become no-ops, and the array's internal subscriptions are not set up. Useful for conditionally enabling a field array in discriminated union form shapes. | -| `keyName` | string = "id" | | Name of the attribute with autogenerated identifier to use as the `key` prop. If your field objects already contain an `id` property (e.g. database records), it will be overwritten by this auto-generated identifier unless you set a custom `keyName`. This prop is no longer required and will be removed in the next major version. | +| `keyName` | string = "id" | | Name of the attribute with autogenerated identifier to use as the `key` prop. If your field objects already contain an `id` property (e.g. database records), it will be overwritten by this auto-generated identifier unless you set a custom `keyName`. This prop is no longer required and is removed in v8, where rows always use `key` (see [v8 migration notes](/migrate-v7-to-v8#useFieldArray)). | | `rules` | Object | | The same validation `rules` API as for [`register`](/docs/useform/register), which includes: `required`, `minLength`, `maxLength`, `validate`. In the case of a validation error, the `root` property is appended to `formState.errors?.?.root` (e.g. `formState.errors?.test?.root` for a field array named `test`), of type [`FieldError`](/ts/#FieldError). **Important:** This is only applicable to **built-in** validation. | **Props example** @@ -60,17 +60,17 @@ function FieldArray() { --- -| Name | Type | Description | -| --------- | ------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `fields` | (object & \{ id: string \})[] | An array whose entries contain the `defaultValue` and `key` for each row in your component. When the hook-level [`disabled`](#disabled) prop is set, every entry's `disabled` property reflects it — but this is not automatically forwarded to the registered input; you need to spread it onto the input yourself (see [Rules](#rules)). | -| `append` | (obj: object \| object[], focusOptions) => void | Append an input or inputs to the end of your fields and focus them. The input value will be registered during this action. **Important:** append data is required and cannot be partial. `focusOptions` accepts `{ shouldFocus?: boolean = true, focusIndex?: number, focusName?: string }`. | -| `prepend` | (obj: object \| object[], focusOptions) => void | Prepend an input or inputs to the start of your fields and focus them. The input value will be registered during this action. **Important:** prepend data is required and cannot be partial. `focusOptions` accepts `{ shouldFocus?: boolean = true, focusIndex?: number, focusName?: string }`. | -| `insert` | (index: number, value: object \| object[], focusOptions) => void | Insert an input or inputs at a particular position and focus them. **Important:** insert data is required and cannot be partial. `focusOptions` accepts `{ shouldFocus?: boolean = true, focusIndex?: number, focusName?: string }`. | -| `swap` | (from: number, to: number) => void | Swap the positions of inputs. | -| `move` | (from: number, to: number) => void | Move an input or inputs to another position. | -| `update` | (index: number, obj: object) => void | Update an input or inputs at a particular position; updated fields will be unmounted and remounted. If this is not the desired behavior, please use the [`setValue`](/docs/useform/setvalue) API instead. **Important:** update data is required and cannot be partial. | -| `replace` | (obj: object[]) => void | Replace the entire field array's values. | -| `remove` | (index?: number \| number[]) => void | Remove an input or inputs at a particular position, or remove all if no index is provided. | +| Name | Type | Description | +| --------- | ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `fields` | (object & \{ id: string \})[] | An array whose entries contain the `defaultValue` and `key` for each row in your component. When the hook-level [`disabled`](#props) prop is set, every entry's `disabled` property reflects it — but this is not automatically forwarded to the registered input; you need to spread it onto the input yourself (see [Rules](#rules)). | +| `append` | (obj: object \| object[], focusOptions) => void | Append an input or inputs to the end of your fields and focus them. The input value will be registered during this action. **Important:** append data is required and cannot be partial. `focusOptions` accepts `{ shouldFocus?: boolean = true, focusIndex?: number, focusName?: string }`. | +| `prepend` | (obj: object \| object[], focusOptions) => void | Prepend an input or inputs to the start of your fields and focus them. The input value will be registered during this action. **Important:** prepend data is required and cannot be partial. `focusOptions` accepts `{ shouldFocus?: boolean = true, focusIndex?: number, focusName?: string }`. | +| `insert` | (index: number, value: object \| object[], focusOptions) => void | Insert an input or inputs at a particular position and focus them. **Important:** insert data is required and cannot be partial. `focusOptions` accepts `{ shouldFocus?: boolean = true, focusIndex?: number, focusName?: string }`. | +| `swap` | (from: number, to: number) => void | Swap the positions of inputs. | +| `move` | (from: number, to: number) => void | Move an input or inputs to another position. | +| `update` | (index: number, obj: object) => void | Update an input or inputs at a particular position; updated fields will be unmounted and remounted. If this is not the desired behavior, please use the [`setValue`](/docs/useform/setvalue) API instead. **Important:** update data is required and cannot be partial. | +| `replace` | (obj: object[]) => void | Replace the entire field array's values. | +| `remove` | (index?: number \| number[]) => void | Remove an input or inputs at a particular position, or remove all if no index is provided. | ### Rules {#rules} @@ -88,6 +88,8 @@ function FieldArray() { {fields.map((field, index) => )} ``` + **v8:** the identifier is renamed from `id` to `key`, so use `key={field.key}`. See the [v8 migration notes](/migrate-v7-to-v8#useFieldArray). + - It's recommended to not stack actions one after another. ```javascript @@ -115,7 +117,7 @@ function FieldArray() { - `shouldUnregister: true` is not supported. Field array relies on inputs being mounted and unmounted to manage its internal state — enabling `shouldUnregister` causes newly added fields to be unregistered on re-render, so their values are lost. Avoid combining `useFieldArray` with `shouldUnregister: true`. -- There is no per-entry `disabled` option — the `disabled` flag on each object in `fields` (see [Return](#return)) simply mirrors the hook-level [`disabled`](#disabled) prop, applied uniformly to every entry. It is not read from data you pass to `append`/`prepend`/`insert`, and it is not automatically forwarded to the registered input — you still need to wire it up yourself: +- There is no per-entry `disabled` option — the `disabled` flag on each object in `fields` (see [Return](#return)) simply mirrors the hook-level [`disabled`](#props) prop, applied uniformly to every entry. It is not read from data you pass to `append`/`prepend`/`insert`, and it is not automatically forwarded to the registered input — you still need to wire it up yourself: ```javascript const { fields, append } = useFieldArray({ diff --git a/src/content/docs/usefieldarray/fieldarray.mdx b/src/content/docs/usefieldarray/fieldarray.mdx index 03147201e..e3c2cb2e3 100644 --- a/src/content/docs/usefieldarray/fieldarray.mdx +++ b/src/content/docs/usefieldarray/fieldarray.mdx @@ -32,7 +32,7 @@ Component based on the [`useFieldArray`](/docs/usefieldarray) hook, useful for w | `control` | Object | | [`control`](/docs/useform/control) object provided by `useForm`. It's optional if you are using `FormProvider`. | | `shouldUnregister` | boolean | | Whether Field Array will be unregistered after unmount. | | `disabled` | boolean | | Disables the entire field array. When `true`, `fields` remains populated but every entry's `disabled` property is set to `true`, all mutation methods (`append`, `prepend`, `insert`, `remove`, `swap`, `move`, `update`, `replace`) become no-ops, and the array's internal subscriptions are not set up. Useful for conditionally enabling a field array in discriminated union form shapes. | -| `keyName` | string = "id" | | Name of the attribute with autogenerated identifier to use as the `key` prop. This prop is no longer required and will be removed in the next major version. | +| `keyName` | string = "id" | | Name of the attribute with autogenerated identifier to use as the `key` prop. This prop is no longer required and is removed in v8, where rows always use `key` (see [v8 migration notes](/migrate-v7-to-v8#useFieldArray)). | | `rules` | Object | | The same validation `rules` API as for [`register`](/docs/useform/register), which includes: `required`, `minLength`, `maxLength`, `validate`. In case of validation error, the `root` property is appended to `formState.errors?.?.root` (e.g. `formState.errors?.test?.root` for a field array named `test`), of type [`FieldError`](/ts/#FieldError). **Important:** This is only applicable to **built-in** validation. | ### Return @@ -41,21 +41,21 @@ Component based on the [`useFieldArray`](/docs/usefieldarray) hook, useful for w The following table contains information about the object passed to the `render` prop function — it's identical to what [`useFieldArray`](/docs/usefieldarray#return) returns. -| Name | Type | Description | -| --------- | ------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `fields` | object & \{ id: string \} | This `object` contains the `defaultValue` and `key` for your component. When the hook-level `disabled` prop is set, every entry's `disabled` property reflects it — but this is not automatically forwarded to the registered input; you need to spread it onto the input yourself. | -| `append` | (obj: object \| object[], focusOptions) => void | Append input/inputs to the end of your fields and focus. The input value will be registered during this action. **Important:** append data is required and not partial. | -| `prepend` | (obj: object \| object[], focusOptions) => void | Prepend input/inputs to the start of your fields and focus. The input value will be registered during this action. **Important:** prepend data is required and not partial. | -| `insert` | (index: number, value: object \| object[], focusOptions) => void | Insert input/inputs at particular position and focus. **Important:** insert data is required and not partial. | -| `swap` | (from: number, to: number) => void | Swap input/inputs position. | -| `move` | (from: number, to: number) => void | Move input/inputs to another position. | -| `update` | (index: number, obj: object) => void | Update input/inputs at a particular position, updated fields will get unmounted and remounted. If this is not desired behavior, please use [`setValue`](/docs/useform/setvalue) API instead. **Important:** update data is required and not partial. | -| `replace` | (obj: object[]) => void | Replace the entire field array values. | -| `remove` | (index?: number \| number[]) => void | Remove input/inputs at particular position, or remove all when no index provided. | +| Name | Type | Description | +| --------- | ------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `fields` | object & \{ id: string \} | This `object` contains the `defaultValue` and `id` for your component. When the hook-level `disabled` prop is set, every entry's `disabled` property reflects it — but this is not automatically forwarded to the registered input; you need to spread it onto the input yourself. | +| `append` | (obj: object \| object[], focusOptions) => void | Append input/inputs to the end of your fields and focus. The input value will be registered during this action. **Important:** append data is required and not partial. | +| `prepend` | (obj: object \| object[], focusOptions) => void | Prepend input/inputs to the start of your fields and focus. The input value will be registered during this action. **Important:** prepend data is required and not partial. | +| `insert` | (index: number, value: object \| object[], focusOptions) => void | Insert input/inputs at particular position and focus. **Important:** insert data is required and not partial. | +| `swap` | (from: number, to: number) => void | Swap input/inputs position. | +| `move` | (from: number, to: number) => void | Move input/inputs to another position. | +| `update` | (index: number, obj: object) => void | Update input/inputs at a particular position, updated fields will get unmounted and remounted. If this is not desired behavior, please use [`setValue`](/docs/useform/setvalue) API instead. **Important:** update data is required and not partial. | +| `replace` | (obj: object[]) => void | Replace the entire field array values. | +| `remove` | (index?: number \| number[]) => void | Remove input/inputs at particular position, or remove all when no index provided. | -- The same rules that apply to [`useFieldArray`](/docs/usefieldarray#rules) apply here — `field.id` (not `index`) must be used as the component `key`, and `shouldUnregister: true` is not supported. +- The same rules that apply to [`useFieldArray`](/docs/usefieldarray#rules) apply here — `field.id` (not `index`) must be used as the component `key`, and `shouldUnregister: true` is not supported. In v8, use `field.key` instead of `field.id`. - `render` is called on every render, so keep it free of side effects. diff --git a/src/content/docs/useform/form.mdx b/src/content/docs/useform/form.mdx index 24217c047..6d9def268 100644 --- a/src/content/docs/useform/form.mdx +++ b/src/content/docs/useform/form.mdx @@ -120,6 +120,7 @@ function App() { {/* Manual form submission */}
{ await fetch("api", { method: "post", diff --git a/src/content/docs/useform/getfieldstate.mdx b/src/content/docs/useform/getfieldstate.mdx index 4a95f7f40..312f3489c 100644 --- a/src/content/docs/useform/getfieldstate.mdx +++ b/src/content/docs/useform/getfieldstate.mdx @@ -73,7 +73,7 @@ This method returns individual field state. It is useful for retrieving nested f const { register, formState } = useForm() const { isDirty } = getFieldState("test", formState) // ✅ formState.isDirty subscribed const { formState } = useFormContext() - const { touchedFields } = getFieldState("test", formState) // ✅ formState.touchedFields subscribed + const { isTouched } = getFieldState("test", formState) // ✅ formState.touchedFields subscribed ``` diff --git a/src/content/docs/useform/getvalues.mdx b/src/content/docs/useform/getvalues.mdx index 34949007f..4a98974f5 100644 --- a/src/content/docs/useform/getvalues.mdx +++ b/src/content/docs/useform/getvalues.mdx @@ -146,19 +146,19 @@ export default function Form() { const { register, getValues } = useForm() getValues() - // function getValues(): Record + // function getValues(): Inputs1 getValues("key1") - // function getValues<"key1", unknown>(payload: "key1"): string + // function getValues<"key1">(name: "key1"): string getValues("key2") - // function getValues<"key2", unknown>(payload: "key2"): number + // function getValues<"key2">(name: "key2"): number getValues("key3.key1") - // function getValues<"key3.key1", unknown>(payload: "key3.key1"): unknown - getValues("key3.key1") - // function getValues(payload: string): number - getValues("key3.key2") - // function getValues(payload: string): boolean + // function getValues<"key3.key1">(name: "key3.key1"): number + getValues("key3.key2") + // function getValues<"key3.key2">(name: "key3.key2"): boolean getValues("key4") - // function getValues<"key4", unknown>(payload: "key4"): string[] + // function getValues<"key4">(name: "key4"): string[] + getValues(["key1", "key3.key2"]) + // function getValues<["key1", "key3.key2"]>(names: readonly ["key1", "key3.key2"]): [string, boolean] return } diff --git a/src/content/docs/useform/register.mdx b/src/content/docs/useform/register.mdx index c1c69ab25..bfa9d1102 100644 --- a/src/content/docs/useform/register.mdx +++ b/src/content/docs/useform/register.mdx @@ -22,19 +22,19 @@ This method allows you to register an input or select element and apply validati --- -| Name | Type | Description | -| ----------- | ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `ref` | React.Ref | React element ref used to connect hook form to the input. | -| `name` | string | Input's name being registered. | -| `onChange` | ChangeHandler | `onChange` prop to subscribe to the input change event. | -| `onBlur` | ChangeHandler | `onBlur` prop to subscribe to the input blur event. | -| `min` | string \| number | Present only when [`progressive`](/docs/useform/form) is `true`; reflects the `min` rule if one was set (otherwise `undefined`). | -| `max` | string \| number | Present only when `progressive` is `true`; reflects the `max` rule if one was set (otherwise `undefined`). | -| `maxLength` | number | Present only when `progressive` is `true`; reflects the `maxLength` rule if one was set (otherwise `undefined`). | -| `minLength` | number | Present only when `progressive` is `true`; reflects the `minLength` rule if one was set (otherwise `undefined`). | -| `pattern` | string | Present only when `progressive` is `true`; reflects the `pattern` rule if one was set (otherwise `undefined`). | -| `required` | boolean | Present only when `progressive` is `true`; reflects the `required` rule if one was set (otherwise `false`). | -| `disabled` | boolean | _optional_. Present when the `disabled` option is set. | +| Name | Type | Description | +| ----------- | ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `ref` | React.Ref | React element ref used to connect hook form to the input. | +| `name` | string | Input's name being registered. | +| `onChange` | ChangeHandler | `onChange` prop to subscribe to the input change event. | +| `onBlur` | ChangeHandler | `onBlur` prop to subscribe to the input blur event. | +| `min` | string \| number | Present only when [`progressive`](/docs/useform#progressive) is `true`; reflects the `min` rule if one was set (otherwise `undefined`). | +| `max` | string \| number | Present only when `progressive` is `true`; reflects the `max` rule if one was set (otherwise `undefined`). | +| `maxLength` | number | Present only when `progressive` is `true`; reflects the `maxLength` rule if one was set (otherwise `undefined`). | +| `minLength` | number | Present only when `progressive` is `true`; reflects the `minLength` rule if one was set (otherwise `undefined`). | +| `pattern` | string | Present only when `progressive` is `true`; reflects the `pattern` rule if one was set (otherwise `undefined`). | +| `required` | boolean | Present only when `progressive` is `true`; reflects the `required` rule if one was set (otherwise `false`). | +| `disabled` | boolean | _optional_. Present when the `disabled` option is set. | @@ -56,7 +56,7 @@ By selecting the register option, the API table below will get updated. -These are validation rules, not native HTML attributes. They run based on the form's [`mode`](/docs/useform#mode) and don't block typing. To also forward `required`, `min`, `max`, `minLength`, `maxLength`, and `pattern` as native attributes on the input, set [`progressive`](/docs/useform/form) to `true` on `useForm`. +These are validation rules, not native HTML attributes. They run based on the form's [`mode`](/docs/useform#mode) and don't block typing. To also forward `required`, `min`, `max`, `minLength`, `maxLength`, and `pattern` as native attributes on the input, set [`progressive`](/docs/useform#progressive) to `true` on `useForm`. diff --git a/src/content/docs/useform/reset.mdx b/src/content/docs/useform/reset.mdx index 616516f07..d9fd85c88 100644 --- a/src/content/docs/useform/reset.mdx +++ b/src/content/docs/useform/reset.mdx @@ -174,7 +174,7 @@ export default function App() { /> - + reset()} /> { @@ -214,7 +214,7 @@ export default function App() { /> - + reset()} /> { diff --git a/src/content/docs/useform/seterror.mdx b/src/content/docs/useform/seterror.mdx index f39eeceeb..ebb4d2058 100644 --- a/src/content/docs/useform/seterror.mdx +++ b/src/content/docs/useform/seterror.mdx @@ -173,7 +173,7 @@ const App = () => { name: "firstName", message: "Triple Check This", }, - ] + ] as const inputs.forEach(({ name, type, message }) => { setError(name, { type, message }) @@ -348,9 +348,9 @@ const App = () => { }); const onSubmit = async () => { const response = await fetch(...) - if (response.statusCode > 200) { + if (response.status >= 400) { setError('root.serverError', { - type: response.statusCode, + type: String(response.status), }) } } @@ -360,7 +360,7 @@ const App = () => { - {errors.root.serverError.type === 400 &&

server response message

} + {errors.root?.serverError?.type === "400" &&

server response message

} diff --git a/src/content/docs/useform/subscribe.mdx b/src/content/docs/useform/subscribe.mdx index 84bed1c2b..d91d7e73a 100644 --- a/src/content/docs/useform/subscribe.mdx +++ b/src/content/docs/useform/subscribe.mdx @@ -15,7 +15,7 @@ Subscribe to [`formState`](/docs/useform/formstate) changes and value updates. Y | Name | Type | Description | Example | | --------- | --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| name | undefined | Subscribe to the entire form | `subscribe()` | +| name | undefined | Subscribe to the entire form | `subscribe({ callback })` | | | string | Subscribe to a single field by **name**. | `subscribe({ name: 'firstName' })` | | | string[] | Subscribe to multiple fields by **name**. | `subscribe({ name: ['firstName', 'lastName'] })` | | formState | `Partial` | Pick which [`formState`](/docs/useform/formstate) to subscribe to. | | @@ -74,7 +74,7 @@ export default function App() { return () => callback() // You can also just return the subscribe - // return subscribe(); + // return subscribe({ callback: ({ values }) => console.log(values) }); }, [subscribe]) return ( diff --git a/src/content/docs/uselens.mdx b/src/content/docs/uselens.mdx index a7c8bc304..6a3416dd9 100644 --- a/src/content/docs/uselens.mdx +++ b/src/content/docs/uselens.mdx @@ -107,8 +107,9 @@ function ContactsList({ lens }: { lens: Lens }) { return (
- ctrl.register(name))} + } />
) @@ -148,13 +149,13 @@ function SharedComponent({ }) { return (
- ctrl.register(name))} + } /> - ctrl.register(name))} + } />
) @@ -254,6 +255,7 @@ function Component({ Maps over array fields with `useFieldArray` integration. This method requires the `fields` property from `useFieldArray`. ```tsx copy +import { Controller } from "react-hook-form" import { useFieldArray } from "@hookform/lenses/rhf" function ContactsList({ lens }: { lens: Lens }) { @@ -282,11 +284,13 @@ function ContactForm({ }) { return (
- ctrl.register(name))} + } /> - ctrl.register(name))} + } />
) @@ -313,8 +317,9 @@ The first variant involves calling `interop()` without arguments, which returns ```tsx const { control, name } = lens.interop() +const { field } = useController({ control, name }) -return +return ``` #### Second Variant: Callback Function @@ -324,7 +329,13 @@ The second variant passes a callback function to `interop` which receives the `c ```tsx return (
- ctrl.register(name))} /> + {lens.interop((control, name) => ( + } + /> + ))} ) @@ -476,8 +487,8 @@ function validateEmail(email: string) { // After confirming value exists if (formState.isValid) { const validEmailLens = emailLens.defined() - // Pass to functions expecting non-null values - validateEmail(validEmailLens.interop().control.getValues()) + // Pass to functions expecting non-null values (getValues from useForm) + validateEmail(getValues(validEmailLens.interop().name)) } ``` @@ -541,6 +552,7 @@ if (config.type === "database") { Import the enhanced `useFieldArray` from `@hookform/lenses/rhf` for seamless array handling with lenses. ```tsx copy +import { Controller } from "react-hook-form" import { useFieldArray } from "@hookform/lenses/rhf" function DynamicForm({ @@ -557,18 +569,19 @@ function DynamicForm({ {itemsLens.map(fields, (field, itemLens, index) => (
- ctrl.register(name))} + } /> - - ctrl.register(name, { valueAsNumber: true }) - )} + ( + field.onChange(e.target.valueAsNumber)} + /> + )} /> {index > 0 && ( @@ -596,7 +609,7 @@ function DynamicForm({ #### Basic Usage ```tsx copy sandbox="https://codesandbox.io/p/sandbox/keen-herschel-y5h2ft" -import { useForm } from "react-hook-form" +import { useForm, useController } from "react-hook-form" import { Lens, useLens } from "@hookform/lenses" import { useFieldArray } from "@hookform/lenses/rhf" @@ -660,7 +673,9 @@ function PersonForm({ } function StringInput({ lens }: { lens: Lens }) { - return ctrl.register(name))} /> + const { field } = useController(lens.interop()) + + return } ``` @@ -694,13 +709,12 @@ Using `useFormContext()` in reusable components tightly couples them to specific ```tsx // ❌ Tightly coupled to parent form structure function AddressForm() { - const { control } = useFormContext() // Locked to UserForm type + const { register } = useFormContext() // Locked to UserForm type return (
- {" "} - {/* Fixed field paths */} - + {/* Fixed field paths */} +
) } @@ -771,7 +785,7 @@ For advanced use cases or when you need more control, you can create lenses manu ```tsx import { useMemo } from "react" -import { useForm } from "react-hook-form" +import { useForm, Controller } from "react-hook-form" import { LensCore, LensesStorage } from "@hookform/lenses" function App() { @@ -784,13 +798,13 @@ function App() { return (
- ctrl.register(name))} + } /> - ctrl.register(name))} + } />
) @@ -801,7 +815,7 @@ function App() { You can extend the basic lens functionality by adding custom methods to the `LensBase` interface. This is useful when you need additional methods that aren't available in the default lens API. -For example, let's add a `getValue` method to the lens that allows you to retrieve the current form values easily. +For example, let's add a `useValue` method to the lens that allows you to subscribe to the current value at the lens path easily. **Step 1: Create the type declarations file** @@ -810,7 +824,7 @@ Create a `lenses.d.ts` file to extend the basic interface with the methods you w ```typescript declare module "@hookform/lenses" { interface LensBase { - getValue(): T + useValue(): T } } @@ -822,12 +836,13 @@ export {} Create a `MyLensCore.ts` file with the actual runtime implementation: ```typescript -import type { FieldValues } from "react-hook-form" +import { useWatch, type FieldValues } from "react-hook-form" import { LensCore } from "@hookform/lenses" export class MyLensCore extends LensCore { - public getValue() { - return this.control._formValues + // Calls a hook, so only call it during render + public useValue() { + return useWatch({ control: this.control, name: this.path }) } } ``` @@ -867,7 +882,7 @@ Now you can use this hook as usual and you have the new method with correct Type ```typescript const { control } = useForm() const lens = useMyLens({ control }) -lens.getValue() // Your custom method is now available with full type support +lens.useValue() // Your custom method is now available with full type support ``` This pattern allows you to add any custom functionality to lenses while maintaining full type safety and compatibility with the existing lens API. diff --git a/src/content/docs/usewatch.mdx b/src/content/docs/usewatch.mdx index f8b459c7b..33b9d3cce 100644 --- a/src/content/docs/usewatch.mdx +++ b/src/content/docs/usewatch.mdx @@ -81,7 +81,7 @@ Behaves similarly to the `watch` API; however, it isolates re-rendering at the c ```tsx copy sandbox="https://codesandbox.io/s/react-hook-form-v7-ts-usewatch-h9i5e" -import { useForm, useWatch } from "react-hook-form" +import { useForm, useWatch, Control } from "react-hook-form" interface FormInputs { firstName: string diff --git a/src/content/ts.mdx b/src/content/ts.mdx index 56d4f8a99..9b28f814c 100644 --- a/src/content/ts.mdx +++ b/src/content/ts.mdx @@ -482,13 +482,18 @@ export type UseFormSubscribe = < The type of the `handleSubmit` function. ```tsx copy +export type SubmitHandler = ( + data: T, + event?: React.BaseSyntheticEvent +) => TResult + export type UseFormHandleSubmit< TFieldValues extends FieldValues, TTransformedValues = TFieldValues, -> = ( - onValid: SubmitHandler, +> = ( + onValid: SubmitHandler, onInvalid?: SubmitErrorHandler -) => (e?: React.BaseSyntheticEvent) => Promise +) => (e?: React.BaseSyntheticEvent) => Promise | undefined> ``` --- @@ -622,10 +627,17 @@ export type UseFormSetFocus = < The type of the `getValues` function. This type is overloaded; it can be called with no arguments, a single field name, or an array of field names. ```tsx copy -export type GetValuesConfig = { - dirtyFields?: boolean - touchedFields?: boolean -} +// Only one of `dirtyFields` or `touchedFields` can be set at a time +type EitherOption = { + [K in keyof T]: { + [P in K]: T[P] + } & Partial, never>> +}[keyof T] + +export type GetValuesConfig = EitherOption<{ + dirtyFields: boolean + touchedFields: boolean +}> export type UseFormGetValues = { (name?: undefined, config?: GetValuesConfig): TFieldValues diff --git a/src/pages/404.tsx b/src/pages/404.tsx index 3096e0236..506994a53 100644 --- a/src/pages/404.tsx +++ b/src/pages/404.tsx @@ -4,7 +4,7 @@ import styles from "./404.module.css" const NotFoundPage = () => ( - +

NOT FOUND

diff --git a/src/pages/[...slug].tsx b/src/pages/[...slug].tsx index e7c961718..f4d33726b 100644 --- a/src/pages/[...slug].tsx +++ b/src/pages/[...slug].tsx @@ -103,32 +103,47 @@ export const getStaticProps = ({ return { notFound: true } } - // Return the doc as page props. - return { props: { doc } } + // Pass only what the page renders. Breadcrumbs and JSON-LD are resolved + // here so `allDocs` (every doc's compiled MDX) stays out of the client bundle. + return { + props: { + doc: { + title: doc.title, + description: doc.description, + metaDescription: doc.metaDescription ?? doc.description, + slug: doc.slug, + code: doc.body.code, + pages: doc.pages as Pages, + sidebarTitle: SIDEBAR_TITLES[doc.sidebar], + breadcrumbs: doc.segment.length > 1 ? getBreadcrumbs(doc) : [], + jsonLd: [getBreadcrumbSchema(doc), getArticleSchema(doc)], + }, + }, + } } export default function Page({ doc, }: InferGetStaticPropsType) { // Parse the MDX file via the useMDXComponent hook. - const MDXContent = useMDXComponent(doc.body.code) + const MDXContent = useMDXComponent(doc.code) return (
- +
- {doc.segment.length > 1 && ( + {doc.breadcrumbs.length > 0 && (