diff --git a/src/components/TabGroup.module.css b/src/components/TabGroup.module.css
index 6b4ead343..04168cd54 100644
--- a/src/components/TabGroup.module.css
+++ b/src/components/TabGroup.module.css
@@ -1,6 +1,7 @@
.buttonTabGroup {
- display: grid;
- grid-auto-flow: column;
+ display: flex;
+ flex-wrap: wrap;
+ gap: 3px;
}
.buttonTabGroup > button {
@@ -12,10 +13,7 @@
transition: var(--transition);
text-transform: uppercase;
letter-spacing: 0.05em;
-}
-
-.buttonTabGroup > button:nth-child(n + 2) {
- margin-left: 3px;
+ flex: 1 1 auto;
}
.buttonTabGroup > button:hover {
diff --git a/src/content/docs/createFormControl.mdx b/src/content/docs/createFormControl.mdx
index 6a12df99b..759595738 100644
--- a/src/content/docs/createFormControl.mdx
+++ b/src/content/docs/createFormControl.mdx
@@ -35,14 +35,24 @@ This function creates the entire form state subscription and allows you to subsc
- - You should either use this API or the Context API
- ```tsx
- const props = createFormControl()
- // ❌ You don't need a provider
+- You should either use this API or the Context API
+
+ ```tsx
+ const props = createFormControl()
+
+ // ❌ You don't need a provider
+
+ // ✅ Use method directly from createFormControl
+ ```
+
+- Create the control once. Passing a new instance to `useForm({ formControl })` re-initializes the form. A control created at module scope is a single form shared by every component that uses it and, during server rendering, by every request. For one form per component instance, create it lazily:
+
+ ```tsx
+ const [form] = useState(() => createFormControl({ defaultValues }))
+ const { register } = useForm({ formControl: form.formControl })
+ ```
- // ✅ Use method directly from createFormControl
- ```
##### Examples:
diff --git a/src/content/docs/usecontroller.mdx b/src/content/docs/usecontroller.mdx
index a1ea883ec..62d39389b 100644
--- a/src/content/docs/usecontroller.mdx
+++ b/src/content/docs/usecontroller.mdx
@@ -40,40 +40,40 @@ The following table contains information about the arguments for `useController`
The following table contains information about properties which `useController` produces.
-| Object Name | Name | Type | Description |
-| ------------ | -------------------- | ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
-| `field` | `onChange` | (value: any) => void | A function which sends the input's value to the library. It should be assigned to the `onChange` prop of the input and value should **not be `undefined`**. This prop updates [formState](/docs/useform/formstate) and you should avoid manually invoking [`setValue`](/docs/useform/setvalue) or other API related to field update. |
-| `field` | `onBlur` | () => void | A function which sends the input's onBlur event to the library. It should be assigned to the input's `onBlur` prop. |
-| `field` | `value` | unknown | The current value of the controlled component. |
-| `field` | `disabled` | boolean | The disabled state of the input. |
-| `field` | `name` | string | Input's name being registered. |
-| `field` | `ref` | React.Ref | A ref used to connect hook form to the input. Assign `ref` to component's input ref to allow hook form to focus the error input. |
-| `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`. |
-| `formState` | `touchedFields` | object | An object containing all the inputs the user has interacted with. |
-| `formState` | `defaultValues` | object | The value that was set in [`useForm`](/docs/useform)'s `defaultValues` or updated `defaultValues` via [`reset`](/docs/useform/reset) API. |
-| `formState` | `isSubmitted` | boolean | Set to `true` after the form is submitted. Will remain `true` until the `reset` method is invoked. |
-| `formState` | `isSubmitSuccessful` | boolean | Indicates that the form was successfully submitted without any runtime error. |
-| `formState` | `isSubmitting` | boolean | `true` if the form is currently being submitted. `false` otherwise. |
-| `formState` | `isLoading` | boolean | `true` if the form is currently loading async default values. **Important:** this prop is only applicable to async `defaultValues`. |
-| `formState` | `submitCount` | number | The number of times the form was submitted. |
-| `formState` | `isValid` | boolean | Set to `true` if the form doesn't have any errors. `setError` immediately forces `isValid` to `false`; this value is not itself derived from validation and will be overwritten the next time validation runs (e.g. on the next `onChange`, submit, or `trigger()` call). |
-| `formState` | `isValidating` | boolean | Set to `true` during validation. |
-| `formState` | `validatingFields` | object | Captures fields that are undergoing asynchronous validation. |
-| `formState` | `errors` | object | An object with field errors. There is also an [ErrorMessage](/docs/useformstate/errormessage) component to retrieve error messages easily. |
-| `formState` | `disabled` | boolean | Set to `true` if the form is disabled via the `disabled` prop in [`useForm`](/docs/useform#disabled). |
-| `formState` | `isReady` | boolean | Set to true when `formState` subscription setup is ready. |
+| Object Name | Name | Type | Description |
+| ------------ | -------------------- | ----------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `field` | `onChange` | (value: any) => void | A function which sends the input's value to the library. It should be assigned to the `onChange` prop of the input and value should **not be `undefined`**. This prop updates [formState](/docs/useform/formstate) and you should avoid manually invoking [`setValue`](/docs/useform/setvalue) or other API related to field update. It accepts either a change event or a value: given an event, it reads `target.checked` for checkboxes, `target.files` for file inputs and `target.value` otherwise. Any object with a `target` property is treated as an event. |
+| `field` | `onBlur` | () => void | A function which sends the input's onBlur event to the library. It should be assigned to the input's `onBlur` prop. |
+| `field` | `value` | unknown | The current value of the controlled component. |
+| `field` | `disabled` | boolean | The disabled state of the input. |
+| `field` | `name` | string | Input's name being registered. |
+| `field` | `ref` | React.Ref | A ref used to connect hook form to the input. Assign `ref` to component's input ref to allow hook form to focus the error input. |
+| `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`. |
+| `formState` | `touchedFields` | object | An object containing all the inputs the user has interacted with. |
+| `formState` | `defaultValues` | object | The value that was set in [`useForm`](/docs/useform)'s `defaultValues` or updated `defaultValues` via [`reset`](/docs/useform/reset) API. |
+| `formState` | `isSubmitted` | boolean | Set to `true` after the form is submitted. Will remain `true` until the `reset` method is invoked. |
+| `formState` | `isSubmitSuccessful` | boolean | Indicates that the form was successfully submitted without any runtime error. |
+| `formState` | `isSubmitting` | boolean | `true` if the form is currently being submitted. `false` otherwise. |
+| `formState` | `isLoading` | boolean | `true` if the form is currently loading async default values. **Important:** this prop is only applicable to async `defaultValues`. |
+| `formState` | `submitCount` | number | The number of times the form was submitted. |
+| `formState` | `isValid` | boolean | Set to `true` if the form doesn't have any errors. `setError` immediately forces `isValid` to `false`; this value is not itself derived from validation and will be overwritten the next time validation runs (e.g. on the next `onChange`, submit, or `trigger()` call). |
+| `formState` | `isValidating` | boolean | Set to `true` during validation. |
+| `formState` | `validatingFields` | object | Captures fields that are undergoing asynchronous validation. |
+| `formState` | `errors` | object | An object with field errors. There is also an [ErrorMessage](/docs/useformstate/errormessage) component to retrieve error messages easily. |
+| `formState` | `disabled` | boolean | Set to `true` if the form is disabled via the `disabled` prop in [`useForm`](/docs/useform#disabled). |
+| `formState` | `isReady` | boolean | Set to true when `formState` subscription setup is ready. |
### Examples {#example}
---
-
+
+
+A generic component built on `useController` works with any form, and TypeScript still checks that `name` is a field of the form that `control` belongs to.
+
+```tsx copy
+import {
+ useForm,
+ useController,
+ UseControllerProps,
+ FieldValues,
+} from "react-hook-form"
+
+function TextInput(props: UseControllerProps) {
+ const { field, fieldState } = useController(props)
+
+ return (
+
+
### Tips
@@ -237,11 +283,23 @@ export default function App() {
const [value, setValue] = useState(field.value);
onChange={(event) => {
- field.onChange(parseInt(event.target.value)) // data sent back to hook form
+ // data sent back to hook form; empty input becomes null instead of NaN
+ field.onChange(event.target.value === "" ? null : event.target.valueAsNumber)
setValue(event.target.value) // UI state
}}
```
+- **Focusing components that don't forward a DOM ref:** `field.ref` accepts any object with a `focus` method (and optionally `select`), so you can point it at the component's own focus API:
+
+ ```javascript
+
+ field.ref(instance && { focus: () => instance.focusInput() })
+ }
+ />
+ ```
+
- Do not `register` the input again. This custom hook is designed to take care of the registration process.
```javascript
diff --git a/src/content/docs/usecontroller/controller.mdx b/src/content/docs/usecontroller/controller.mdx
index a060b6e2b..c696d536a 100644
--- a/src/content/docs/usecontroller/controller.mdx
+++ b/src/content/docs/usecontroller/controller.mdx
@@ -34,34 +34,34 @@ The following table contains information about the arguments for `Controller`.
The following table contains information about properties which `Controller` produces.
-| Object Name | Name | Type | Description |
-| -------------------------------------- | -------------------- | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `field` | `onChange` | `(value: any) => void` | A function which sends the input's value to the library.
_It should be assigned to the `onChange` prop of the input and the value should **not be `undefined`**._ This prop updates [formState](/docs/useform/formstate) and you should avoid manually invoking [setValue](/docs/useform/setvalue) or other APIs related to field updates. |
-| `field` | `onBlur` | `() => void` | A function which sends the input's onBlur event to the library. It should be assigned to the input's `onBlur` prop. |
-| `field` | `value` | unknown | The current value of the controlled component. |
-| `field` | `disabled` | boolean | The disabled state of the input. |
-| `field` | `name` | string | Input's name being registered. |
-| `field` | `ref` | React.Ref | A ref used to connect hook form to the input. Assign `ref` to component's input ref to allow hook form to focus the error input — this only works if the target component forwards refs (`React.forwardRef`) or exposes an equivalent, like MUI's `inputRef`. |
-| `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.
**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.
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`
**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.
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.
|
-| `formState` | `touchedFields` | object | An object containing all the inputs the user has interacted with. |
-| `formState` | `defaultValues` | object | The value that was set in [useForm](/docs/useform)'s `defaultValues` or updated `defaultValues` via the [reset](/docs/useform/reset) API. |
-| `formState` | `isSubmitted` | boolean | Set to `true` after the form has been submitted. Will remain `true` until the `reset` method is invoked. |
-| `formState` | `isSubmitSuccessful` | boolean | Indicates that the form was successfully submitted without any runtime error. |
-| `formState` | `isSubmitting` | boolean | `true` if the form is currently being submitted. `false` otherwise. |
-| `formState` | `isLoading` | boolean | `true` if the form is currently loading async default values. **Important:** this prop is only applicable to async `defaultValues` |
-| `formState` | `submitCount` | number | The number of times the form was submitted. |
-| `formState` | `isValid` | boolean | Set to `true` if the form doesn't have any errors.
`setError` immediately forces `isValid` to `false`; this value is not itself derived from validation and will be overwritten the next time validation runs (e.g. on the next `onChange`, submit, or `trigger()` call). |
-| `formState` | `isValidating` | boolean | Set to `true` during validation. |
-| `formState` | `validatingFields` | object | Captures fields that are undergoing asynchronous validation. |
-| `formState` | `errors` | object | An object with field errors. There is also an [ErrorMessage](/docs/useformstate/errormessage) component to retrieve error messages easily. |
-| `formState` | `disabled` | boolean | Set to `true` if the form is disabled via the `disabled` prop in [`useForm`](/docs/useform#disabled). |
-| `formState` | `isReady` | boolean | Set to true when `formState` subscription setup is ready. |
+| Object Name | Name | Type | Description |
+| -------------------------------------- | -------------------- | ------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `field` | `onChange` | `(value: any) => void` | A function which sends the input's value to the library.
_It should be assigned to the `onChange` prop of the input and the value should **not be `undefined`**._ This prop updates [formState](/docs/useform/formstate) and you should avoid manually invoking [setValue](/docs/useform/setvalue) or other APIs related to field updates. It accepts either a change event or a value: given an event, it reads `target.checked` for checkboxes, `target.files` for file inputs and `target.value` otherwise. Any object with a `target` property is treated as an event. |
+| `field` | `onBlur` | `() => void` | A function which sends the input's onBlur event to the library. It should be assigned to the input's `onBlur` prop. |
+| `field` | `value` | unknown | The current value of the controlled component. |
+| `field` | `disabled` | boolean | The disabled state of the input. |
+| `field` | `name` | string | Input's name being registered. |
+| `field` | `ref` | React.Ref | A ref used to connect hook form to the input. Assign `ref` to component's input ref to allow hook form to focus the error input — this only works if the target component forwards refs (`React.forwardRef`) or exposes an equivalent, like MUI's `inputRef`. |
+| `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.
**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.
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`
**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.
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.
|
+| `formState` | `touchedFields` | object | An object containing all the inputs the user has interacted with. |
+| `formState` | `defaultValues` | object | The value that was set in [useForm](/docs/useform)'s `defaultValues` or updated `defaultValues` via the [reset](/docs/useform/reset) API. |
+| `formState` | `isSubmitted` | boolean | Set to `true` after the form has been submitted. Will remain `true` until the `reset` method is invoked. |
+| `formState` | `isSubmitSuccessful` | boolean | Indicates that the form was successfully submitted without any runtime error. |
+| `formState` | `isSubmitting` | boolean | `true` if the form is currently being submitted. `false` otherwise. |
+| `formState` | `isLoading` | boolean | `true` if the form is currently loading async default values. **Important:** this prop is only applicable to async `defaultValues` |
+| `formState` | `submitCount` | number | The number of times the form was submitted. |
+| `formState` | `isValid` | boolean | Set to `true` if the form doesn't have any errors.
`setError` immediately forces `isValid` to `false`; this value is not itself derived from validation and will be overwritten the next time validation runs (e.g. on the next `onChange`, submit, or `trigger()` call). |
+| `formState` | `isValidating` | boolean | Set to `true` during validation. |
+| `formState` | `validatingFields` | object | Captures fields that are undergoing asynchronous validation. |
+| `formState` | `errors` | object | An object with field errors. There is also an [ErrorMessage](/docs/useformstate/errormessage) component to retrieve error messages easily. |
+| `formState` | `disabled` | boolean | Set to `true` if the form is disabled via the `disabled` prop in [`useForm`](/docs/useform#disabled). |
+| `formState` | `isReady` | boolean | Set to true when `formState` subscription setup is ready. |
##### Examples:
@@ -224,19 +224,32 @@ The following video showcases what's inside Controller and how it was built.
/>
```
-- Customise what value gets sent to hook form by transforming the value during `onChange`.
+- Customise what value gets sent to hook form by transforming the value during `onChange`. For number inputs, map an empty input to `null` instead of `NaN`, and render `null` as an empty string. `rules` doesn't support `valueAsNumber`, so convert the value yourself.
+
```javascript
{
- // sending integer instead of string.
- return (
- field.onChange(parseInt(e.target.value))}
- />
- )
- }}
+ name="age"
+ render={({ field }) => (
+
+ field.onChange(e.target.value === "" ? null : e.target.valueAsNumber)
+ }
+ />
+ )}
+ />
+ ```
+
+- **Focusing components that don't forward a DOM ref:** `field.ref` accepts any object with a `focus` method (and optionally `select`), so you can point it at the component's own focus API:
+
+ ```javascript
+
+ field.ref(instance && { focus: () => instance.focusInput() })
+ }
/>
```
diff --git a/src/content/docs/usefieldarray.mdx b/src/content/docs/usefieldarray.mdx
index 3e65d65a8..ad07f67ca 100644
--- a/src/content/docs/usefieldarray.mdx
+++ b/src/content/docs/usefieldarray.mdx
@@ -167,7 +167,7 @@ function FieldArray() {
---
-
+
+ )
+}
+
+export default function App() {
+ const { control, register, handleSubmit } = useForm({
+ defaultValues: {
+ teams: [{ name: "", members: [{ email: "" }] }],
+ },
+ })
+ const { fields, append, remove } = useFieldArray({
+ control,
+ name: "teams",
+ })
+
+ return (
+
+ )
+}
+```
+
### Video
@@ -534,6 +620,8 @@ function App() {
There will be cases where you want to control the entire field array, which means each onChange reflects on the `fields` object.
+If you only need to display values from the array, such as a live total, read them with [`useWatch`](/docs/usewatch) (`useWatch({ control, name: "fieldArray" })`) in a child component instead, so only that component re-renders.
+
```tsx sandbox="https://codesandbox.io/s/infallible-bush-c92l0?file=/src/App.tsx"
import { useForm, useFieldArray } from "react-hook-form"
diff --git a/src/content/docs/useform.mdx b/src/content/docs/useform.mdx
index c7f634153..ca0d655c7 100644
--- a/src/content/docs/useform.mdx
+++ b/src/content/docs/useform.mdx
@@ -171,6 +171,21 @@ useForm({
- You **should avoid** providing `undefined` as a default value, as it conflicts with the default state of a controlled component.
- `defaultValues` are cached. To reset them, use the [reset](/docs/useform/reset) API.
+- An async `defaultValues` function runs once, when the form mounts. If its promise rejects, `formState.isLoading` stays `true`, so catch errors inside the function and return fallback values:
+
+ ```javascript
+ useForm({
+ defaultValues: async () => {
+ try {
+ const res = await fetch("/api/user")
+ return await res.json()
+ } catch {
+ return { firstName: "", lastName: "" }
+ }
+ },
+ })
+ ```
+
- `defaultValues` will be included in the submission result by default.
- It's recommended to avoid using custom objects containing prototype methods, such as `Moment` or `Luxon`, as `defaultValues`.
- There are other options for including form data:
@@ -219,6 +234,22 @@ function App() {
}
```
+
+
+- `values` is deep-compared with the previous `values` prop. When it changes, the form is reset with the new values, which **replaces anything the user has typed**. If your data source refetches in the background (for example, React Query on window focus) and returns updated data, set `resetOptions: { keepDirtyValues: true }` to keep the user's edits.
+- Passing a new object with the same content does nothing, so you don't need to memoize `values`.
+
+
+
+##### Loading form data from a server: which option to use?
+
+| Approach | Updates when the data changes | Good for |
+| --------------------------------------------- | ------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
+| Async [`defaultValues`](#defaultValues) | No, it runs once on mount | Edit forms that load their data once. `formState.isLoading` is `true` until it resolves. |
+| [`values`](#values) | Yes, it resets the form whenever the data changes | Forms that must stay in sync with external or server state. Combine with [`resetOptions`](#resetOptions) to keep user edits. |
+| [`reset`](/docs/useform/reset) in `useEffect` | Only when your effect runs | Full control over when the form is refilled and which state to keep. |
+| Render the form after the data arrives | No | The simplest option: fetch in the parent and pass the data in as `defaultValues`. |
+
#### errors: FieldErrors {#errors}
---
diff --git a/src/content/docs/useform/formstate.mdx b/src/content/docs/useform/formstate.mdx
index 635335d74..139d4dfb6 100644
--- a/src/content/docs/useform/formstate.mdx
+++ b/src/content/docs/useform/formstate.mdx
@@ -26,10 +26,10 @@ This object contains information about the entire form state. It helps you keep
| `defaultValues` | object | The value that was set at [useForm](/docs/useform)'s `defaultValues` or updated `defaultValues` via the [reset](/docs/useform/reset) API. |
| `isSubmitted` | boolean | Set to `true` after the form is submitted. Will remain `true` until the `reset` method is invoked. |
| `isSubmitSuccessful` | boolean | Indicates that the form was successfully submitted without any runtime error. |
-| `isSubmitting` | boolean | `true` if the form is currently being submitted. `false` otherwise. |
+| `isSubmitting` | boolean | `true` if the form is currently being submitted. `false` otherwise.
It stays `true` only while the promise returned by your `onSubmit` handler is pending. If you start a request without `await`ing or returning it, `isSubmitting` turns `false` straight away.
|
| `isLoading` | boolean | `true` if the form is currently loading async default values.
**Important:** this prop is only applicable to async `defaultValues` await fetch('/api')\n})`}/>
|
| `submitCount` | number | The number of times the form has been submitted. |
-| `isValid` | boolean | Set to `true` if the form doesn't have any errors.
`setError` immediately forces `isValid` to `false`; this value is not itself derived from validation and will be overwritten the next time validation runs (e.g. on the next `onChange`, submit, or `trigger()` call).
|
+| `isValid` | boolean | Set to `true` if the form doesn't have any errors.
While `isValid` is subscribed, every field (or the whole [resolver](/docs/useform#resolver) schema) is validated on mount and on each change, whatever the [`mode`](/docs/useform#mode). `errors` only fills in according to `mode` and `reValidateMode`, so with the default `mode: "onSubmit"` `isValid` can be `false` while `errors` is still empty.
`isValid` is not computed while the form is [`disabled`](/docs/useform#disabled).
`setError` immediately forces `isValid` to `false`; this value is not itself derived from validation and will be overwritten the next time validation runs (e.g. on the next `onChange`, submit, or `trigger()` call).
|
| `isValidating` | boolean | Set to `true` during validation. |
| `validatingFields` | object | Captures fields that are undergoing asynchronous validation. |
| `errors` | object | An object with field errors. There is also an [ErrorMessage](/docs/useformstate/errormessage) component to retrieve error messages easily. |
diff --git a/src/content/docs/useform/getvalues.mdx b/src/content/docs/useform/getvalues.mdx
index 4a98974f5..99df94814 100644
--- a/src/content/docs/useform/getvalues.mdx
+++ b/src/content/docs/useform/getvalues.mdx
@@ -45,6 +45,8 @@ The example below shows what to expect when you invoke `getValues` method.
- It will return `defaultValues` from `useForm` before the **initial** render.
+- `getValues` returns the raw field values. The data passed to your `handleSubmit` callback can differ: with a [resolver](/docs/useform#resolver) it's the resolver's output (with any transforms or coercion applied), and disabled fields are left out.
+- `getValues` doesn't subscribe to changes, so it won't re-render your component. To render a value, use [`useWatch`](/docs/usewatch) or [`watch`](/docs/useform/watch).
diff --git a/src/content/docs/useform/handlesubmit.mdx b/src/content/docs/useform/handlesubmit.mdx
index 148494613..36021b8c7 100644
--- a/src/content/docs/useform/handlesubmit.mdx
+++ b/src/content/docs/useform/handlesubmit.mdx
@@ -29,6 +29,14 @@ This function will receive the form data if form validation is successful.
const result = await handleSubmit(onValid)() // result: { id: string } | undefined
```
+- `formState.isSubmitting` stays `true` until the promise returned by `onSubmit` settles, so `await` (or return) your async work:
+
+ ```javascript
+ const onSubmit = async (data) => {
+ await fetch("/api", { method: "POST", body: JSON.stringify(data) }) // ✅ isSubmitting stays true until this resolves
+ }
+ ```
+
- `disabled` inputs will appear as `undefined` values in form values. If you want to prevent users from updating an input and wish to retain the form value, you can use `readOnly` or disable the entire <fieldset />. Here is an [example](https://codesandbox.io/s/react-hook-form-disabled-inputs-oihxx).
- `handleSubmit` function will not swallow errors that occurred inside your onSubmit callback, so we recommend that you use try/catch blocks inside your async requests and handle those errors gracefully for your customers. Use `setError` inside the catch block to register a server-side error — this also ensures `formState.isSubmitSuccessful` is set to `false`.
@@ -48,6 +56,28 @@ This function will receive the form data if form validation is successful.
return
```
+- To submit from a button outside the `
+
+ // ✅ native form attribute
+ // ✅ call handleSubmit directly
+ ```
+
+- React's `submit` event bubbles through the component tree, including portals. If a form is rendered inside another form (for example in a modal), stop propagation so the outer form doesn't submit too:
+
+ ```javascript
+