Skip to content

useFormState

Subscribe to form state update

useFormState: ({ control: Control }) => FormState

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

Important: returned formState is wrapped with Proxy to improve render performance and skip extra logic if specific state is not subscribed, so make sure you deconstruct or read it before render in order to enable the subscription.

const { isDirty } = useFormState(); // ✅
              
const formState = useFormState(); // ❌ should deconstruct the formState
formState.isDirty; // ❌ subscription will be one render behind.      

Props

The following table contains information about the arguments for useFormState.

NameTypeRequiredDescription
controlobjectcontrol object from useForm.
namestring | string[]Provide a single input name, an array of them, or subscribe to all inputs' formState update.
disableboolean = false

Option to disable the subscription.

Return

NameTypeDescription
isDirtyboolean

Set to true after the user modifies any of the inputs.

  • 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.
  • File typed input will need to be managed at the app level due to the ability to cancel file selection and FileList object.

  • Native inputs will return string type by default.

  • isDirty state will be affected with actions from useFieldArray. For example below:

    useForm({ defaultValues: { test: [] } })
    const { append } = useFieldArray({ name: 'test' })
    
    // append will make form dirty, because a new input is created
    // and form values is no longer deeply equal defaultValues.
    append({ firstName: '' }); 
    
dirtyFieldsobject

An object with the user-modified fields. Make sure to provide all inputs' defaultValues via useForm, so the library can compare against the defaultValues.

touchedFieldsobjectAn object containing all the inputs the user has interacted with.
isSubmittedbooleanSet to true after the form is submitted. Will remain true until the reset method is invoked.
isSubmitSuccessfulboolean

Indicate the form was successfully submitted without any Promise rejection or Error been threw within the handleSubmit callback.

isSubmittingbooleantrue if the form is currently being submitted. false if otherwise.
submitCountnumberNumber of times the form was submitted.
isValidboolean
Set to true if the form doesn't have any errors.

Note: isValid is affected by mode at useForm. This state is only applicable with onChange and onBlur mode.

isValidatingbooleanSet to true during validation.
errorsobjectAn object with field errors. There is also an ErrorMessage component to retrieve error message easily.
import * as React from "react";
import { useForm, useFormState } from "react-hook-form";

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

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register("firstName")} placeholder="First Name" />
      {dirtyFields.firstName && <p>Field is dirty.</p>}
      
      <input type="submit" />
    </form>
  );
}
Edit