Skip to content

Latest commit

 

History

History
238 lines (198 loc) · 26 KB

File metadata and controls

238 lines (198 loc) · 26 KB
title Controller
description Wrapper component for controlled inputs
sidebar apiLinks

</> Controller: Component

React Hook Form embraces uncontrolled components and native inputs, however it's hard to avoid working with external controlled component such as React-Select, AntD and MUI. This wrapper component will make it easier for you to work with them.

Note: if you simply want to control a field's value from outside the form, it's not necessary to use Controller. You can simply use the values option of useForm.

Props


The following table contains information about the arguments for Controller.

Name Type Required Description
name FieldPath Unique name of your input.
control Control control object is from invoking useForm. Optional when using FormProvider.
render Function This is a render prop. 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 onChange, onBlur, name, ref and value to the child component, and also a fieldState object which contains specific input state.
defaultValue unknown Important: Can not apply undefined to defaultValue or defaultValues at useForm.
  • You need to either set defaultValue at the field-level or useForm's defaultValues. undefined is not a valid value.
  • If your form will invoke reset with default values, you will need to provide useForm with defaultValues.
  • Calling onChange with undefined is not valid. You should use null or the empty string as your default/cleared value instead.
rules Object Validation rules in the same format for 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.

Return


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 value should not be undefined.
This prop update formState and you should avoid manually invoke 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 error object error for this specific input.
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, so hook form can have a single source of truth to compare whether the form is dirty.
  2. File typed input will need to be managed at the app level due to the ability to cancel file selection and 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, so hook form 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 field level rather 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 which has been set at useForm's defaultValues or updated defaultValues via 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 Indicate 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 Number of times the form was submitted.
formState isValid boolean Set to true if the form doesn't have any errors.

setError has no effect on isValid formState, isValid will always derived via the entire form validation result.
formState isValidating boolean Set to true during validation.
formState errors object An object with field errors. There is also an ErrorMessage component to retrieve error message easily.

Examples:


Web

<TabGroup buttonLabels={["TS", "JS"]}>

import ReactDatePicker from "react-datepicker"
import { TextField } from "@material-ui/core"
import { useForm, Controller } from "react-hook-form"

type FormValues = {
  ReactDatepicker: string
}

function App() {
  const { handleSubmit, control } = useForm<FormValues>()

  return (
    <form onSubmit={handleSubmit((data) => console.log(data))}>
      <Controller
        control={control}
        name="ReactDatepicker"
        render={({ field: { onChange, onBlur, value, ref } }) => (
          <ReactDatePicker
            onChange={onChange} // send value to hook form
            onBlur={onBlur} // notify when input is touched/blur
            selected={value}
          />
        )}
      />

      <input type="submit" />
    </form>
  )
}
import React from "react"
import ReactDatePicker from "react-datepicker"
import { TextField } from "@material-ui/core"
import { useForm, Controller } from "react-hook-form"

function App() {
  const { handleSubmit, control } = useForm()

  return (
    <form onSubmit={handleSubmit((data) => console.log(data))}>
      <Controller
        control={control}
        name="ReactDatepicker"
        render={({ field: { onChange, onBlur, value, ref } }) => (
          <ReactDatePicker
            onChange={onChange}
            onBlur={onBlur}
            selected={value}
          />
        )}
      />

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

React Native

import { Text, View, TextInput, Button, Alert } from "react-native"
import { useForm, Controller } from "react-hook-form"

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

  return (
    <View>
      <Controller
        control={control}
        rules={{
          required: true,
        }}
        render={({ field: { onChange, onBlur, value } }) => (
          <TextInput
            placeholder="First name"
            onBlur={onBlur}
            onChangeText={onChange}
            value={value}
          />
        )}
        name="firstName"
      />
      {errors.firstName && <Text>This is required.</Text>}

      <Controller
        control={control}
        rules={{
          maxLength: 100,
        }}
        render={({ field: { onChange, onBlur, value } }) => (
          <TextInput
            placeholder="Last name"
            onBlur={onBlur}
            onChangeText={onChange}
            value={value}
          />
        )}
        name="lastName"
      />

      <Button title="Submit" onPress={handleSubmit(onSubmit)} />
    </View>
  )
}

Video


The following video showcases what's inside Controller and how its been built.

  • It's important to be aware of each prop's responsibility when working with external controlled components, such as MUI, AntD, Chakra UI. Controller acts as a "spy" on your input by reporting and setting value.

    • onChange: send data back to hook form
    • onBlur: report input has been interacted (focus and blur)
    • value: set up input initial and updated value
    • ref: allow input to be focused with error
    • name: give input an unique name The following codesandbox demonstrate the usages:
    • MUI and other components
    • Chakra UI components
  • Do not register input again. This component is made to take care of the registration process.

    <Controller
      name="test"
      render={({ field }) => {
        // return <input {...field} {...register('test')} />; ❌ double up the registration
        return <input {...field} /> // ✅
      }}
    />
  • Customise what value gets sent to hook form by transforming the value during onChange.

    <Controller
      name="test"
      render={({ field }) => {
        // sending integer instead of string.
        return (
          <input
            {...field}
            onChange={(e) => field.onChange(parseInt(e.target.value))}
          />
        )
      }}
    />