# useFormState

> Subscribe to specific form state properties and isolate re-renders at the component level for better performance in large React Hook Forms.

Source: https://react-hook-form.com/docs/useformstate

## `useFormState:` ([UseFormStateProps](https://react-hook-form.com/ts#UseFormStateProps)) => [FormState](https://react-hook-form.com/ts#FormState)

This custom hook allows you to subscribe to each form state, and isolate re-renders at the custom hook level. It has its own scope in terms of form state subscription, so it does not affect other `useFormState` or `useForm` hooks. Using this hook can reduce the re-render impact on large and complex form applications.

### Props

---

| Name       | Type                                     | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| ---------- | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `control`  | `Object`              | [`control`](https://react-hook-form.com/docs/useform/control) object provided by `useForm`. It's optional if you are using `FormProvider`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `name`     | `string \| string[]` | _(since v7.4.0)_ Provide a single input name, an array of them, or subscribe to all inputs' formState updates.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `disabled` | `boolean = false`     | _(since v7.13.0)_ Option to disable the subscription.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `exact`    | `boolean = false`     | _(since v7.20.0)_ Enable exact name matching. When `false` (default), a subscription fires when the subscribed name is a prefix of the changed field name, or vice versa (for example, subscribing to `"users"` receives updates for `"users.0.name"`). _(since v7.81.0)_ When `true`, the subscription fires when the subscribed field itself or one of its ancestor paths is updated (for example, subscribing to `"users.0.name"` receives updates when `"users.0"` or `"users"` is set via `setValue`), but not when a nested child path changes (subscribing to `"users"` does not receive updates for `"users.0.name"`). |

### Return

---

| Name                 | Type                         | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| -------------------- | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `isDirty`            | `boolean` | Set to `true` after the user modifies any of the inputs.<ul><li>**Important:** make sure to provide all inputs' `defaultValues` at the `useForm`, so hook form can have a single source of truth to compare whether the form is dirty.<pre><code>const {<br/>  formState: { isDirty, dirtyFields },<br/>  setValue<br/>} = useForm({ defaultValues: { test: "" } })<br/><br/>// isDirty: true ✅<br/>setValue('test', 'change')<br/><br/>// isDirty: false because there getValues() === defaultValues ❌<br/>setValue('test', '')</code></pre></li><li>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.</li><li>Does not support custom objects, classes, or File objects.</li></ul> |
| `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.`<ul><li>**Important:** make sure to provide `defaultValues` at the `useForm`, so hook form can have a single source of truth to compare each field's dirtiness.</li><li>`dirtyFields` does not necessarily match `isDirty`, because fields are marked dirty at the field level rather than the entire form — for example, adding or removing entries in a field array can change `isDirty` without any individual field in `dirtyFields` being marked dirty. If you want to determine the entire form state use `isDirty` instead.</li></ul>                                                                                                  |
| `touchedFields`      | `object`  | An object containing all the inputs the user has interacted with.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `defaultValues`      | `object`  | _(since v7.37.0)_ The value that was set in [useForm](https://react-hook-form.com/docs/useform)'s `defaultValues` or updated `defaultValues` via [reset](https://react-hook-form.com/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.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| `isLoading`          | `boolean` | _(since v7.41.0)_ `true` if the form is currently loading async default values.<ul><li>**Important:** this prop is only applicable to async `defaultValues`<pre><code>const {<br/>  formState: { isLoading }<br/>} = useForm({<br/>  defaultValues: async () =&gt; await fetch('/api')<br/>})</code></pre></li></ul>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `submitCount`        | `number`  | The number of times the form was submitted.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `isValid`            | `boolean` | Set to `true` if the form doesn't have any errors.<ul><li>`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).</li><li>Without a `resolver` or a validation `mode` other than the default `onSubmit`, `isValid` won't reflect actual validity until validation has run at least once (e.g. via `trigger()` or a submit attempt).</li></ul>                                                                                                                                                                                                                                                                                                  |
| `isValidating`       | `boolean` | Set to `true` during validation.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| `validatingFields`   | `object`  | _(since v7.51.0)_ Captures fields that are undergoing asynchronous validation.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `errors`             | `object`  | An object with field errors. There is also an [ErrorMessage](https://react-hook-form.com/docs/useformstate/errormessage) component to retrieve error messages easily.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `disabled`           | `boolean` | _(since v7.48.0)_ Set to `true` if the form is disabled via the [`disabled`](https://react-hook-form.com/docs/useform#disabled) prop in [useForm](https://react-hook-form.com/docs/useform).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `isReady`            | `boolean` | _(since v7.56.0)_ Set to true when `formState` subscription setup is ready.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |

> **Important: Rules**
>
> Returned `formState` is wrapped with a Proxy to improve render performance and skip extra computation if a specific state is not subscribed to; make sure you destructure or read it before rendering to enable the subscription.
>
> ```javascript
> const { isDirty } = useFormState() // ✅
> const formState = useFormState() // ❌ should destructure the formState
> ```
>

### Examples

---

[Open in CodeSandbox](https://codesandbox.io/s/useformstate-75xly)

```javascript
import { useForm, useFormState } from "react-hook-form"

function Child({ control }) {
  const { dirtyFields } = useFormState({ control })

  return dirtyFields.firstName ? <p>Field is dirty.</p> : null
}

export default function App() {
  const { register, handleSubmit, control } = useForm({
    defaultValues: {
      firstName: "firstName",
    },
  })
  const onSubmit = (data) => console.log(data)

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register("firstName")} placeholder="First Name" />
      <Child control={control} />

      <input type="submit" />
    </form>
  )
}
```

**Submit button in a `FormProvider`**

A shared submit button can read `isSubmitting` and `isValid` with `useFormState`. Only the button re-renders when they change. Reading `formState` from `useFormContext()` here instead is a common source of buttons that don't update (see the rules on [`useFormContext`](https://react-hook-form.com/docs/useformcontext)).

```tsx
import {
  useForm,
  FormProvider,
  useFormState,
  useFormContext,
} from "react-hook-form"

function SubmitButton() {
  const { isSubmitting, isValid } = useFormState()

  return (
    <button type="submit" disabled={isSubmitting || !isValid}>
      {isSubmitting ? "Saving…" : "Save"}
    </button>
  )
}

function NameInput() {
  const { register } = useFormContext<{ name: string }>()
  return <input {...register("name", { required: true })} />
}

export default function App() {
  const methods = useForm<{ name: string }>({ defaultValues: { name: "" } })

  return (
    <FormProvider {...methods}>
      <form
        onSubmit={methods.handleSubmit(async (data) => {
          await fetch("/api", { method: "POST", body: JSON.stringify(data) })
        })}
      >
        <NameInput />
        <SubmitButton />
      </form>
    </FormProvider>
  )
}
```

### Tips

---

- Put `useFormState` in the leaf component that shows the state, such as a submit button or an error summary. Only that component re-renders:

  ```javascript
  function SubmitButton({ control }) {
    const { isDirty, isSubmitting } = useFormState({ control })
    return <button disabled={!isDirty || isSubmitting}>Save</button>
  }
  ```

- Pass `name` to scope the subscription to one field or one section. Use it with [`getFieldState`](https://react-hook-form.com/docs/useform/getfieldstate) to read that field's `error`, `isDirty`, and `isTouched`.
- You can call `useFormState` more than once in the same component. Each call has its own subscription and does not affect the others.

### Troubleshooting

---

#### `Cannot read properties of null (reading '_formState')`

**Why it happens:**

`useFormState` could not find a form: no `control` was passed and the component is not inside a [`FormProvider`](https://react-hook-form.com/docs/formprovider).

**Fix:**

Pass `control` from `useForm`, or render the component inside `<FormProvider {...methods}>`.
