Skip to content

Latest commit

 

History

History
1890 lines (1318 loc) · 39.1 KB

CHANGELOG.md

File metadata and controls

1890 lines (1318 loc) · 39.1 KB

Changelog

[7.51.0] - 2024-3-2

Added

  • added 'validateFields' to formState
const { formState: { validateFields } } = useForm();

[7.49.0] - 2023-12-10

Added

  • add reactive errors prop at useForm
useForm({
  errors, // Server errors
});

[7.48.0] - 2023-11-05

Added

  • added new disabled prop for useForm to disable the entire form
const App = () => {
  const [disabled, setDisabled] = useState(false);
  const { handleSubmit } = useForm({ disabled });

  return (
    <form
      onSubmit={handleSubmit(async () => {
        setDisabled(true);
        await sleep(100);
        setDisabled(false);
      })}
    / >
  );
}

[7.47.0] - 2023-10-02

Added

  • reset api with keepIsSubmitSuccessful option, keep successfully submitted form state.
<Form
  onSubmit={() => {
    reset(formValues, {
      keepIsSubmitSuccessful: true,
    });
  }}
/>

[7.46.0] - 2023-09-03

Added

  • Controller disabled prop
const [disabled, setDisabled] = useState(false);
useController({
  disabled,
});
  • Trigger passed names to construct resolver options
  • Add exact option for array name in useWatch

Changed

  • Update isDirty when setting disabled in register

Fixed

  • Prevent reset argument mutation

[7.45.0] - 2023-06-20

Changed

  • Controller with type check on onChange
- onChange: (...event: any[]) => void;
+ onChange: (event: ChangeEvent | FieldPathValue<TFieldValues, TName>) => void;
  • Include missing generic for useFormContext
- export const useFormContext: <TFieldValues extends FieldValues, TransformedValues extends FieldValues | undefined = undefined>() => UseFormReturn<TFieldValues>;
+ export const useFormContext: <TFieldValues extends FieldValues, TContext = any, TransformedValues extends FieldValues | undefined = undefined>() => UseFormReturn<TFieldValues, TContext, TransformedValues>;

[7.44.0] - 2023-06-14

Added

  • New <Form /> component
// Send post request with formData
<Form
  action="/api"
  control={control}
  onSuccess={() => {
    alert("Great");
  }}
/>

// Send post request with json form data
<Form action="/api" encType="application/json" headers={{ accessToken: 'test' }}>
  {errors.root?.server.type === 500 && 'Error message'}
  {errors.root?.server.type === 400 && 'Error message'}
</Form>

// Send post request with formData with fetch
<Form
  onSubmit={async ({ formData, data, formDataJson, event }) => {
    await fetch("api", {
      method: "post",
      body: formData,
    });
  }}
/>
  • support TransformedValues with useFormContext

useFormContext<FormValue, TransformedValues>()

  • added TTransformedValues to FormProvider

FormProviderProps<TFieldValues, TContext, TTransformedValues>

[7.43.0] - 2023-01-30

Added

  • support global error type
const onSubmit = async () => {
  setError('root.serverError', {
    type: response.statusCode,
  });
};
const onClick = () => {
  setError('root.random', {
    type: 'random',
  });
};

return (
  <>
    {errors.root.serverError.type === 400 && <p>server response message</p>}
    <p>{errors.root?.random?.message}</p>
  </>
);

[7.42.0] - 2023-01-13

Added

  • build in validation validate support second argument for form values
// Making exported validate function isolated for validation
export function validateNumber(_: number, formValus: FormValues) {
  return formValus.number1 + formValus.number2 === 3;
}

<input
  type="number"
  {...register('number1', {
    validate: validateNumber,
    valueAsNumber: true,
  })}
/>;

Changed

  • handleSubmit no longer catch onSubmit callback error
  • Remove deprecated for fieldState.invalid

[7.41.0] - 2022-12-17

Added

  • useForm added values props
const values = await fetch('API');

useForm({
  values, // will reset the form when values updates
  // resetOptions: {
  //   keepDirtyValues: true
  // }
});
  • new isLoading formState for async defaultValues
const {
  formState: { isLoading },
} = useForm();

Changed

  • useForm support async defaultValues props
const {
  formState: { isLoading },
} = useForm({
  defaultValues: fetch('API'),
  // resetOptions: {
  //   keepDirtyValues: true
  // }
});

[7.40.0] - 2022-11-30

Changed

  • async validation (or combined with sync) will always the take the latest validation result and abort the previous

[7.39.5] - 2022-11-21

Changed

  • Conditional render useFormState will trigger an extra re-render to reflect the current formState

[7.39.0] - 2022-11-2

Changed

  • isValid formState is no longer only applicable with onChange, onTouched, and onBlur mode.

[7.38.0] - 2022-10-19

Added

  • support build-in validation with input type week and time
<input {...register("week", { min: "2022-W40" })} type="week" />
<input {...register("time", { min: "11:00" })} type="time" />

[7.37.0] - 2022-10-07

Added

  • new formState defaultValues
const { formState, watch } = useForm({
  defaultValues: { name: 'test' },
});
const { defaultValues } = useFormState();

const name = watch('name');

return (
  <div>
    <p>Your name was {defaultValues.name}</p>
    <p>Updated name is {name}</p>
  </div>
);

Changed

  • defaultValues: complex object data contains prototype methods will not be cloned internally

[7.36.0] - 2022-9-20

Added

  • reset to support callback syntax
reset((formValues) => {
  return {
    ...formValues,
    partialData: 'onlyChangeThis',
  };
});

[7.35.0] - 2022-9-10

Added

  • new type FieldPathByValue field path by value generic implementation
function CustomFormComponent<
  TFieldValues extends FieldValues,
  Path extends FieldPathByValue<TFieldValues, Date>,
>({ control, name }: { control: Control<FieldValues>; name: Path }) {
  const { field } = useController({
    control,
    name,
  });
}

function App() {
  const { control } = useForm<{
    foo: Date;
    baz: string;
  }>();

  return (
    <form>
      <CustomFormComponent control={control} name="foo" /> {/* no error */}
      <CustomFormComponent control={control} name="baz" /> {/*  throw an error since baz is string */}
    </form>
  );
}

Changed

  • form context support children prop type
<FormProvider {...methods}>
  <div /> // ✅
  <div /> // ✅
</FormProvider>

[7.34.0] - 2022-7-28

Added

  • Build in validation support for useFieldArray with rules prop
useFieldArray({
  name: 'test',
  rules: {
    required: true,
    minLength: 2,
    maxLength: 10,
    validate: (fieldArrayValues) => {
      if (fieldArrayValues[2].title === 'test') {
        return 'validate Error';
      }
    },
  },
});

errors?.test?.root?.message; // access root level errors

[7.33.0] - 2022-6-24

Breaking Change

  • @hookform/resolvers needs to upgraded to version ^2.9.3 above
  • useFormContext do always required to provide a generic type check for your form, without providing generic will now require developers to convert error messages to String to pass the type check
useFormContext<FormValues>(); // ✅ correct usage by provide form type defination

const { formState } = useFormContext(); // if generic is missing
String(formState.errors?.input?.message); // will need to convert to string

Changed

  • Deprecate NestedValue and UnpackNestedValue type, will be removed in the next major version. Important: If you are using them, it may cause TS compile error, so please just remove the type usage.
type FormValues = {
-  select: NestedValue<{
-    nested: string
-  }>
+  select: {
+    nested: string
+  }
}

type Data = UnpackNestedValue<FieldValues>
  • formState's errors is now mapped/merged with FieldError
  • UseFormHandleSubmit has removed unused function generic

[7.32.0] - 2022-6-10

Changed

  • UseFormRegisterReturn name type change from string to TFieldName

[7.31.0] - 2022-5-11

Added

  • new: reset optional prop: keepDirtyValues
reset(
  {
    firstName: 'bill', // if firstName is dirty then the value will be retained
    lastName: 'luo',
  },
  { keepDirtyValues: true }, // keep any changed field
);

Changed

  • useFieldArray auto-correct field array errors on user action
const { append } = useFieldArray();

append({ data: '' }); // will auto correct existing field array errors if any

[7.30.0] - 2022-4-17

Changed

  • improve checkboxes value determine by defaultValues
useForm({
  defaultValues: {
    checkboxes: [], // register checkbox will be determine as array of checkboxes
  },
});
register('checkboxes'); // will return array as value

[7.29.0] - 2022-3-30

Changed

  • tsconfig config change from es2017 to es2018

[7.28.0] - 2022-3-13

Changed

  • register API options deps now support string
register('test', { deps: 'test' });

[7.27.0] - 2022-2-11

Added

  • new option for setFocus to select the entire field value
setFocus('fieldName', { shouldSelect: true });

[7.25.2] - 2022-1-29

Changed

  • onTouched mode will honor focusout event

[7.25.0] - 2022-1-22

Added

  • getFieldState get individual field state
export default function App() {
  const {
    register,
    getFieldState,
    formState: { isDirty, isValid },
  } = useForm({
    mode: 'onChange',
    defaultValues: {
      firstName: '',
    },
  });

  // you can invoke before render or within the render function
  const fieldState = getFieldState('firstName');

  return (
    <form>
      <input {...register('firstName', { required: true })} />
      <p>{getFieldState('firstName').isDirty && 'dirty'}</p>
      <p>{getFieldState('firstName').isTouched && 'touched'}</p>
      <button
        type="button"
        onClick={() => console.log(getFieldState('firstName'))}
      >
        field state
      </button>
    </form>
  );
}

[7.24.0] - 2022-1-14

Changed

  • useController return prop: onChange, onBlur and ref will be memorized with useCallback

[7.23.0] - 2022-1-12

Changed

  • useFieldArray change keyName is no longer required when field value contains id
const App = () => {
  const { control, register, handleSubmit } = useForm<FormValues>({
    defaultValues: {
      test: [{ id: 'UUID5678', test: 'data' }], // id value will be retained
    },
  });
  const { fields, append } = useFieldArray({
    control,
    name: 'test',
  });

  return (
    <form>
      {fields.map((field, index) => {
        return <input key={field.id} {...register(`test.${index}.test`)} />;
      })}

      <button
        type={'button'}
        onClick={() => {
          append({
            id: 'UUID1234', // id value will be retained
            test: '1234',
          });
        }}
      >
        append
      </button>
    </form>
  );
};
  • useFormState will no longer fire state update after hook unmount
  • UseFormHandleSubmit type will infer formValues

[7.22.0] - 2021-12-14

Changed

  • Browser native reset API will no longer be invoked when reset provided with value
const onSubmit = (data) => {};

React.useEffect(() => {
  if (formState.isSubmitSuccessful) {
    reset({ something: '' });
  }
}, [formState, reset]);

handleSubmit(onSubmit);

to

const onSubmit = (data) => {
  setSubmittedData(data);
  reset(data); // no longer need to have useEffect
};

handleSubmit(onSubmit);

[7.21.0] - 2021-12-06

Changed

  • shouldUseNativeValidation will pass down validation props both at client and server render
const { register } = useForm()

<input {...register('name', { required: true })} />

<input name="name" required /> // both client and server render

[7.20.3] - 2021-11-26

Changed

  • register onChange will share same logic with useController for non standard event payload
const { onChange } = register('test');
onChange('stringIsValid'); // this is only valid use case for JS
  • empty node in formState will no longer gets unset

[7.20.0] - 2021-11-19

Added

  • new exact prop for useWatch
  • new exact prop for useFormState
useWatch({
  name: 'test.test',
  exact: true,
});

useFormState({
  name: 'test.test',
  exact: true,
});

Changed

  • useWatch subscription will occurred at useEffect instead before render

[7.19.0] - 2021-11-05

Added

  • new resetField API
const { resetField } = useForm();

resetField('test');

resetField('test', {
  keepError: true,
  keepDirty: true,
  keepTouched: true,
  defaultValue: 'test1',
});

Changed

  • useController will return shallow clone value for the following data type on each rerender
    • object: {... value}
    • array: [...value]

[7.18.1] - 2021-11-02

  • revert FieldPathWithValue

[7.18.0] - 2021-10-28

Added

  • bring back FieldPathWithValue
  • schema errors parent object look up
const validationSchema = object().shape({
  questions: array().min(1, 'Array cannot be empty'),
});

// the above schema will be picked up by field array action
// the logic applies to group error object eg checkboxes
<button
  type="button"
  onClick={() => {
    remove(questionIndex);
  }}
>
  Remove
</button>;

[7.17.0] - 2021-10-02

Added

  • new type FieldPathWithValue to improve generic components type support
type ExpectedType = { test: string };

const Generic = <FormValues extends FieldValues>({
  name,
  control,
}: {
  name: FieldPathWithValue<FormValues, ExpectedType>;
  control: Control<FormValues>;
}) => {
  const {
    field: { value, ...fieldProps },
  } = useController<FormValues, ExpectedType>({
    name,
    control,
    defaultValue: { test: 'value' },
  });

  return <input type="text" value={value.test} {...fieldProps} />;
};

[7.16.1] - 2021-09-27

Changed

  • formState subscription no longer subscribed at useEffect instead the execution of each hook

[7.16.0] - 2021-09-25

Added

  • register allowed pass custom onChange and onBlur
<input
  type="text"
  {...register('test', {
    onChange: (e) => {},
    onBlur: (e) => {},
  })}
/>

[7.15.0] - 2021-09-05

Added

  • useFieldArray new method replace()
const { control } = useForm({
  defaultValues: {
    test: [{ value: 'lorem' }, { value: 'ipsum' }],
  },
});
const { fields, replace } = useFieldArray({
  control,
  name: 'test',
});

const handleFullyReplacement = (): void => {
  // remove old and set fully new values
  replace([{ value: 'dolor' }, { value: 'sit' }, { value: 'amet' }]);
};
  • Improved to not map types defined with interface.
import { useForm } from 'react-hook-form';

interface CustomValue {
  custom: string;
}

type FormValues = {
  fieldName: CustomValue;
};

const { formState: errors } = useForm<FormValues>({
  defaultValues: { fieldName: { custom: 'value' } },
});

[7.14.0] - 2021-08-27

Added

  • register add dependent validation
const App = () => {
  const { register, getValues } = useForm();

  return (
    <form>
      <input
        {...register('firstName', {
          validate: (value) => {
            return getValues('lastName') === value;
          },
        })}
      />
      <input {...register('lastName', { deps: ['firstName'] })} /> // dependant validation
    </form>
  );
};

[7.13.0] - 2021-08-22

Added

Trigger

  • Trigger will enable object name trigger and field array name trigger
useFieldArray({ name: 'test' });

trigger('name'); // will trigger the whole field array to validate

register

  • added a disabled prop as an option to toggle input disable attribute
  • register will be able to seek input DOM reference through the ref callback
register('test', { disabled: true }) // will set value to undefined and pass disabled down to the input attribute

<div {...register('test')}>
  <input name="test" /> // this input will be registered
</div>

useWatch

  • added disabled prop to toggle the state subscription.
useWatch({ disabled: true }); // you toggle the subscription

useFormState

  • added disabled prop to toggle the state subscription.
useFormState({ disabled: true }); // you toggle the subscription

setValue

  • allow set value for non-registered inputs include nested object and array field.
<input {...register('test.0.firstName')} />

setValue('test', [{ firstName: 'bill' }, {firstName: 'kotaro}, {firstName: 'joris'}]) // this will works

[7.12.0] - 2021-07-24

Added

  • new useForm config delayError
useForm({
  delayError: 500, // delay error appear with 500ms
});

[7.11.0] - 2021-07-13

Added

  • update method to update an field array inputs
const { update } = useFieldArray();

update(0, data); // update an individual field array node

[7.10.0] - 2021-07-02

Changed

  • defaultValue is no longer a required prop for register input with useFieldArray

[7.9.0] - 2021-06-19

Added

  • new config at useForm to enabled native browser validation
const { register, handleSubmit } = useForm({
  shouldUseNativeValidation: true,
});

[7.8.5] - 2021-06-15

Change

  • useController no longer access input ref except focus event for focus management

[7.8.0] - 2021-06-5

Added

  • setValue support shouldTouch to update formState touchFields
setValue('firstName', 'value', { shouldTouch: true });
  • register now accept value as option
register('firstName', { value: 'value' });

Changed

  • isValid will initialise as false

[7.7.1] - 2021-05-30

Fixed

  • shouldUnregister: false should not shallow merge or register absent input fields from defaultValues

[7.7.0] - 2021-05-29

Added

  • trigger support focus with error input
trigger('inputName', { shouldFocus: true });

Changed

  • handleSubmit will throw error within the onSubmit callback

[7.6.0] - 2021-05-15

Changed

  • useForm will register missing inputs from defaultValues
const App = () => {
  const { register, handleSubmit } = useForm({
    defaultValues: {
      test: { firstName: 'bill', lastName: 'luo' },
    },
  });

  const onSubmit = (data) => {
    // missing registered input will be included
    console.log(data); // { test: { firstName: 'bill', lastName: 'luo' } }
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register('test.firstName')} />
      <button />
    </form>
  );
};

[7.5.0] - 2021-05-09

Changed

  • isSubmitSuccessful will return false when handleSubmit callback failed with Error or Promise reject.
  • unmounted input will no longer get validated even with shouldUnregister: false

[7.4.0] - 2021-05-04

Added

  • new name prop for useFormState to subscribe to individual inputs.
useFormState({
  name: 'inputName', // optional and can be array of inputs' name as well
});

[7.2.2] - 2021-04-21

Changes

  • set shouldUnregister to true will not shallow merge defaultValues

[7.2.0] - 2021-04-19

Changes

  • shouldUnregister config to remove input value after unmount
// Global config (can't be overwrite)
useForm({
  shouldUnregister: true // default to false
})

// Component/Hook level config (can not overwrites global config)
register('test', {
  shouldUnregister: true // default to false
})

<Controller  shouldUnregister={true} />

useController({ shouldUnregister: true })

useFieldArray({ shouldUnregister: true })

[7.0.6] - 2021-04-12

Changes

  • register will retrieve onChange's target value when component'ref is not a valid input element.

[7.0.0-rc.7] - 2021-03-28

Changes

  • change type name from RefCallbackHandler to UseFormRegisterReturn for register callback's return

[7.0.0-rc.7] - 2021-03-23

Changes

  • useFieldArray will produce an empty array [] when no field is presented.

[7.0.0-rc.1] - 2021-03-08

Changes

  • setValue with field array will register all inputs before rendering.

[7.0.0-beta.17] - 2021-03-03

Changes

  • append, prepend and insert will register deeply nested inputs at useFieldArray.

[7.0.0-beta.15] - 2021-02-27

Changes

  • typescript array index restriction removed.
  • append, prepend and insert will register inputs during each action at useFieldArray.

[7.0.0-beta.11] - 2021-02-20

Changes

  • change ArrayKey type to number | '${number}'

[7.0.0-beta.10] - 2021-02-19

Changes

  • Change useController's meta into fieldState and include formState, these change will be applied to Controller too.
- const { field, meta } = useController({ control });
+ const { field, fieldState, formState } = useController({ control });

[7.0.0-beta.9] - 2021-02-19

Changes

  • typescript array index support is changed to 49 instead of 99

[7.0.0-beta.4] - 2021-02-08

  • Breaking change: valueAs will be run before the built-in validation and resolver
- <input {...register('test', { validate: (data: string) => {}, valueAsNumber: true })} />
+ <input {...register('test', { validate: (data: number) => {}, valueAsNumber: true })} />

[7.0.0-beta.1] - 2021-02-08

Changes

  • useWatch will no longer required defaultValue for field Array

[7.0.0-beta.0] - 2021-02-06

Changes

  • Breaking change: shallow merge defaultValues with result (#4074)
useForm({ defaultValues: { test: 'test' } });

getValues(); // v6 will return {}
getValues(); // v7 will return { test: 'test' }

[v7.0.0-alpha.2] - 2021-02-04

Changes

  • Breaking change: setError's shouldFocus option has been moved into the third argument.
- setError('test', { type: 'type', message: 'issue', shouldFocus: true })
+ setError('test', { type: 'type', message: 'issue' }, { shouldFocus: true })
  • Breaking change: type name changes:
- UseFormMethods
+ UseFormReturn
- UseFormOptions
+ UseFormProps
- UseFieldArrayMethods
+ UseFieldArrayReturn
- UseFieldArrayOptions
+ UseFieldArrayProps
- UseControllerMethods
+ UseControllerReturn
- UseControllerOptions
+ UseControllerProps
- ArrayField
+ FieldArray

Fixes

  • fix setValue with Controller and reset with useFieldArray issues: 4111 & 4108 (#4113)

[v7.0.0-alpha.2] - 2021-02-02

Changes

  • Breaking change: setError's shouldFocus option has been moved to the third argument.
- setError('test', { type: 'type', message: 'issue', shouldFocus: true })
+ setError('test', { type: 'type', message: 'issue' }, { shouldFocus: true })

Fixes

  • fix #4078 issue with watch + mode: onChange

Improvements

  • remove internal deep clone (#4088)
  • remove transformToNestObject (#4089)

[v7.0.0-alpha.1] - 2021-02-01

Changes

  • field name reference will be removed with unregister (#4010)

  • Breaking change: improve field array remove result and no longer remove field array value after unmount

const { remove } = useFieldArray({ name: 'test' })
remove();
getValues(); // V6: result form value {}
getValues(); // V7: result form value { test: [] }

Improvements

  • change internal field names into Set (#4015)
  • improve onChange perf with `resolver (#4017)
  • improve field array name look up perf (#4030)

[v7.0.0-alpha.0] - 2021-01-31

Added

  • new custom hook useFormState (#3740)
const { isDirty, errors } = useFormState();
  • watch support can subscribe to the entire form with a callback
watch((data, { name, type }) => {
  console.log('formValue', data);
  console.log('name', name);
  console.log('type', type);
});
  • useController includes new isValidating state (#3778)
  • useController includes new error state (#3921)
const {
  meta: { error, isValidating },
} = useController({ name: 'test' });
  • new unregister second argument (#3964)
unregister('test', { keepDirty: true });
  • Resolver add field being validated (#3881)
- resolver: (values: any, context?: object) => Promise<ResolverResult> | ResolverResult
+ resolver: (
+    values: any,
+    context?: object,
+    options: {
+       criteriaMode?: 'firstError' | 'all',
+       names?: string[],
+       fields: { [name]: field } // Support nested field
+    }
+  ) => Promise<ResolverResult> | ResolverResult
  • useFieldArray action can focus input by name and index
append(object, config: { shouldDirty: boolean, focusIndex: number, focusName: string })
insert(object, config: { shouldDirty: boolean, focusIndex: number, focusName: string })
prepend(object, config: { shouldDirty: boolean, focusIndex: number, focusName: string })

Changes

  • Breaking change: No longer support IE 11 support

  • Breaking change: register has been changed from register at ref to a function which needs to be spread as props.

- <input ref={register, { required: true }} name="test" />
+ <input {...register('name', { required: true })} />
+ <TextInput {...register('name', { required: true })} />
  • Breaking change: name with array will only support dot syntax instead of brackets.
- test[2].test
+ test.2.test
  • Breaking change: remove as prop at Controller and fix render prop consistency (#3732)
- <Controller render={props => <input {...props} />} />
+ <Controller render={({ field }) => <input {...field} />} />
  • Breaking change: remove errors alias (#3737)
- const { errors } = useForm();
+ const { formState: { errors } } = useForm();
  • Breaking change: improved reset second argument (#3905)
- reset({}, { isDirty: true })
+ reset({}, { keepIsDirty: true })
  • Breaking change: change touched to touchedFields for consistency (#3923)
- const { formState: { touched } } = useForm();
+ const { formState: { touchedFields }} = useForm();
  • Breaking change: trigger will no longer return validation result.
- await trigger('test') // return true or false
+ trigger('test') // void
  • remove isSubmitting proxy (#4000)

  • input register will no longer be removed due to unmount, user will have to manually invoke unregister

Improvements

  • useWatch internal mechanism improvement (#3754)
  • Controller and useController apply useFormState internally and improve performance (#3778)
  • register type support for input name (#3738)
  • Controller and useCOntroller type support for input name (#3738)
  • useFieldArray internal logic and data structure improvement (#3858)
  • improve useFieldArray internal fields update with subscription (#3943)
  • improve tests structure (#3916)
  • useWatch type improvement (#3931)
  • improve type support for nested field array with const (#3920)
  • improve useFieldArray internal type structure (#3986)
  • MutationObserver removed from useForm

[6.15.0] - 2021-02-02

Changed

  • radio input default selection will return null instead of empty string ''
  • valueAsNumber with empty input will return NaN instead of 0

[6.14.0] - 2020-12-31

Changed

  • setValue without shouldUnregister:false will no longer deep clone its value instead with shallow clone

Added

  • new formState isValidating, this will set to true during validation.
const {
  formState: { isValidating },
} = useForm();

[6.12.0] - 2020-12-12

Changed

  • When invoking reset({ value }) value will be shallow clone value object which you have supplied instead of deepClone.
// ❌ avoid the following with deep nested default values
const defaultValues = { object: { deepNest: { file: new File() } } };
useForm({ defaultValues });
reset(defaultValues); // share the same reference

// ✅ it's safer with the following, as we only doing shallow clone with defaultValues
useForm({ deepNest: { file: new File() } });
reset({ deepNest: { file: new File() } });

Added

  • New custom hook useController: This custom hook is what powers Controller, and shares the same props and methods as Controller. It's useful to create reusable Controlled input, while Controller is the flexible option to drop into your page or form.
import React from 'react';
import { TextField } from '@material-ui/core';
import { useController } from 'react-hook-form';

function Input({ control, name }) {
  const {
    field: { ref, ...inputProps },
    meta: { invalid, isTouched, isDirty },
  } = useController({
    name,
    control,
    rules: { required: true },
    defaultValue: '',
  });

  return <TextField {...inputProps} inputRef={ref} />;
}

[6.12.0] - 2020-11-28

Changed

  • useWatch will retrieve the latest value from reset(data) instead of return defaultValue
useWatch({
  name: 'test',
  defaultValue: 'data', // this value will only show on the initial render
});
  • TS: name changed from ValidationRules to RegisterOptions due to valueAs functionality included as register function.

Added

  • register function with additional options to transform value

    • valueAsDate
    • valueAsNumber
    • setValueAs
register({
  valueAsNumber: true,
});

register({
  valueAsNumber: true,
});

register({
  setValueAs: (value) => value,
});

Added

[6.11.0] - 2020-11-07

Changed

  • defaultValues is required to measure isDirty, keep a single source of truth to avoid multiple issues raised around isDirty
  • when watch with useFieldArray, fields object is no longer required as defaultValue
- watch('fieldArray', fields);
+ watch('fieldArray');

[6.10.0] - 2020-10-31

Added

  • Controller will have an extra ref props to improve DX in terms of focus management.
<Controller
  name="test"
  render={(props) => {
    return (
      <input
        value={props.value}
        onChange={props.onChange}
        ref={props.ref} // you can assign ref now without the use of `onFocus`
      />
    );
  }}
/>

// focus will work correct without the `onFocus` prop
<Controller name="test" as={<input />} />

Changed

  • resolver with group error object will no longer need with trigger to show and clear error. This minor version made hook form look at parent error node to detect if there is any group error to show and hide.
const schema = z.object({
  items: z.array(z.boolean()).refine((items) => items.some((item) => item)),
});

{
  items.map((flag, index) => (
    <input
      type="checkbox"
      defaultChecked={false}
      // onChange={() => trigger("items")} now can be removed
      ref={register}
      name={`items.${index}`}
    />
  ));
}

[6.9.0] - 2020-10-3

Changed

  • with shouldUnregister set to false, empty Field Array will default [] as submission result.
const { handleSubmit } = useForm({
  shouldUnregister: false,
});

useFieldArray({
  name: 'test',
});

handleSubmit((data) => {
  // shouldUnregister: false
  // result:  { data: {test: []} }
  // shouldUnregister: true
  // result: {}
});

[6.8.4] - 2020-09-22

Changed

  • when input unmounts touched and dirtyFields will no longer get removed from formState (shouldUnregister: true).

[6.8.0] - 2020-09-09

Added

  • new formState isSubmitSuccessful to indicate successful submission
  • setError now support focus on the actual input
setError('test', { message: 'This is required', shouldFocus: true });

Changed

  • with shouldUnregister:false defaultValues data will be part of the submission data
  • with shouldUnregister:false conditional field is going to work with useFieldArray
  • setValue now support useFieldArray
- setValue('test', 'data')
+ setValue('test', [{ test: '123' }]) // make it work for useFieldArray and target a field array key
  • remove exact config at clearErrors
- clearErrors('test', { exact: false })
+ clearErrors('test') // does it automatically in the lib

[6.7.0] - 2020-08-30

Added

  • clearError have second option argument for clear errors which are exact or key name
register('test.firstName', { required: true });
register('test.lastName', { required: true });
clearErrors('test', { exact: false }); // will clear both errors from test.firstName and test.lastName
clearErrors('test.firstName'); // for clear single input error

[6.6.0] - 2020-08-28

Changed

[6.5.0] - 2020-08-23

Changed

  • errors is also part of formState object
  • disabled input will not be part of the submission data by following the HTML standard

[6.4.0] - 2020-08-15

Added

  • Controller's render prop will pass down name prop
  • handleSubmit take a second callback for errors callback
  • new mode onTouched will only trigger validation after inputs are touched

Changed

  • register no longer compare ref difference with React Native

[6.3.2] - 2020-08-11

Changed

  • IE 11 version will be required to install @babel/runtime-corejs3 as dependency at your own project

[6.3.0] - 2020-08-8

Changed

  • defaultValue is become required for useFieldArray at each input

[6.2.0] - 2020-07-30

Changed

  • revert getValues will return default values before inputs registration

[6.1.0] - 2020-07-26

Changed

  • resolver supports both async and sync
  • getValues will return default values before inputs registration

[6.0.7] - 2020-07-17

Added

  • export ArrayField type

Changed

  • error message will support array of messages for specific type
- export type ValidateResult = Message | boolean | undefined;
+ export type ValidateResult = Message | Message[] | boolean | undefined;

[6.0.3] - 2020-07-10

Changed

  • Controller onFocus works with React Native
  • Controller stop producing checked prop by boolean value

[6.0.2] - 2020-07-8

Added

  • export UseFormOptions, UseFieldArrayOptions, FieldError, Field and Mode type

[6.0.1] - 2020-07-3

Added

  • export ValidationRules type

[6.0.0] - 2020-07-1

Added

  • config for shouldUnregister which allow input to be persist even after unmount
useForm({
  shouldUnregister: false, // unmount input state will be remained
});
  • auto focus with useFieldArray
append({}, (autoFocus = true));
prepend({}, (autoFocus = true));
insert({}, (autoFocus = true));
  • TS: NestedValue
import { useForm, NestedValue } from 'react-hook-form';

type FormValues = {
  key1: string;
  key2: number;
  key3: NestedValue<{
    key1: string;
    key2: number;
  }>;
  key4: NestedValue<string[]>;
};

const { errors } = useForm<FormValues>();

errors?.key1?.message; // no type error
errors?.key2?.message; // no type error
errors?.key3?.message; // no type error
errors?.key4?.message; // no type error
  • useWatch (new) subscribe to registered inputs.
<input name="test" ref={register} />;

function IsolateReRender() {
  const { state } = useWatch({
    name: 'test',
    control,
    defaultValue: 'default',
  });

  return <div>{state}</div>;
}
  • getValues() support array of field names
getValues(['test', 'test1']); // { test: 'test', test1: 'test1' }
  • useForm({ mode: 'all' }) support all validation

Changed

  • rename validationResolver to resolver

  • rename validationContext to context

  • rename validateCriteriaMode to criteriaMode

  • rename triggerValidation to trigger

  • rename clearError to clearErrors

  • rename FormContext to FormProvider

  • rename dirty to isDirty

  • dirtyFields change type from Set to Object

  • Controller with render props API, and removed the following props:

    • onChange
    • onChangeName
    • onBlur
    • onBlurName
    • valueName
-<Controller
-  as={CustomInput}
-  valueName="textValue"
-  onChangeName="onTextChange"
-  control={control}
-  name="test"
-/>
+<Controller
+  render={({ onChange, onBlur, value }) => (
+     <CustomInput onTextChange={onChange} onBlur={onBlur} textValue={value} />
+  )}
+  control={control}
+  name="test"
+/>
  • setError will focus one error at a time and remove confusing set multiple errors, behavior change.
    • setError will persist an error if it's not part of the form, which requires manual remove with clearError
    • setError error will be removed by validation rules, rules always take over errors
- setError('test', 'test', 'test')
+ setError('test', { type: 'test', message: 'bill'})
  • setValue will focus on input at a time
setValue('test', 'value', { shouldValidate: false, shouldDirty: false })

Removed

  • remove validationSchema and embrace validation resolver
  • remove nest option for watch & getValues, so data return from both methods will be in FormValues shape.
-getValues({ nest: true }); // { test: { data: 'test' }}
-watch({ nest: true }); // { test: { data: 'test' }}
+getValues(); // { test: { data: 'test' }}
+watch(); // { test: { data: 'test' }}

[5.0.0] - 2020-03-07

Breaking Change

  • Controller: onChange will only evaluate payload as event like object. eg: react-select will no longer need the extra onChange method at Controller.
import { TextInput } from 'react-native';

-<Controller
-  as={<TextInput style={{ borderWidth: 2, borderColor: 'black'}} />}
-  name="text"
-  control={args => ({
-    value: args[0].nativeEvent.text,
-  })}
-  onChange={onChange}
-/>
+<Controller
+  as={<TextInput style={{ borderWidth: 2, borderColor: 'black'}} />}
+  name="text"
+  control={args => args[0].nativeEvent.text}
+  onChange={onChange}
+/>

[4.0.0] - 2019-12-24

Breaking changes

  • improve module exports:
import { useForm } from 'react-hook-form';
  • nested errors object and better typescript support
type form = {
  yourDetail: {
    firstName: string;
  };
};

errors?.yourDetail?.firstName;
  • triggerValidation argument change from Object, Object[] to String, String[]
triggerValidation('firstName');
triggerValidation(['firstName', 'lastName']);
  • watch support { nest: boolean }
watch(); // { 'test.firstName': 'bill' }
watch({ nest: true }); // { test: { firstName: 'bill' } }
  • improve custom register
register('test', { required: true });
  • setError` support nested object
setError('yourDetail.firstName', 'test');
errors.yourDetails.firstName;
  • handleSubmit no longer rerun array inputs contains undefined or null

Added

  • move RHFInput into the main repo and rename it to Controller
<Controller control={control} name="test" />

Removed

[3.0.0] - 2019-04-21

Added

React Hook Form return a new formState: Object which contain the following information

  • dirty: when user interactive any fields
  • touched: what are the fields have interacted
  • isSubmitted: whether the form have been triggered with submitting
const {
  formState: { dirty, touched, isSubmitted },
} = useForm();

[2.0.0] - 2019-03-29

Added

  • support ref={register} instead of only ref={register()}