react-hook-form-6
Version:
Latest version 6 of react-hook-form
1,185 lines (1,129 loc) • 82.9 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var React = require('react');
var isHTMLElement = (value) => value instanceof HTMLElement;
const EVENTS = {
BLUR: 'blur',
CHANGE: 'change',
INPUT: 'input',
};
const VALIDATION_MODE = {
onBlur: 'onBlur',
onChange: 'onChange',
onSubmit: 'onSubmit',
onTouched: 'onTouched',
all: 'all',
};
const SELECT = 'select';
const UNDEFINED = 'undefined';
const INPUT_VALIDATION_RULES = {
max: 'max',
min: 'min',
maxLength: 'maxLength',
minLength: 'minLength',
pattern: 'pattern',
required: 'required',
validate: 'validate',
};
function attachEventListeners({ ref }, shouldAttachChangeEvent, handleChange) {
if (isHTMLElement(ref) && handleChange) {
ref.addEventListener(shouldAttachChangeEvent ? EVENTS.CHANGE : EVENTS.INPUT, handleChange);
ref.addEventListener(EVENTS.BLUR, handleChange);
}
}
var isNullOrUndefined = (value) => value == null;
const isObjectType = (value) => typeof value === 'object';
var isObject = (value) => !isNullOrUndefined(value) &&
!Array.isArray(value) &&
isObjectType(value) &&
!(value instanceof Date);
var isKey = (value) => /^\w*$/.test(value);
var compact = (value) => value.filter(Boolean);
var stringToPath = (input) => compact(input
.replace(/["|']/g, '')
.replace(/\[/g, '.')
.replace(/\]/g, '')
.split('.'));
function set(object, path, value) {
let index = -1;
const tempPath = isKey(path) ? [path] : stringToPath(path);
const length = tempPath.length;
const lastIndex = length - 1;
while (++index < length) {
const key = tempPath[index];
let newValue = value;
if (index !== lastIndex) {
const objValue = object[key];
newValue =
isObject(objValue) || Array.isArray(objValue)
? objValue
: !isNaN(+tempPath[index + 1])
? []
: {};
}
object[key] = newValue;
object = object[key];
}
return object;
}
var transformToNestObject = (data, value = {}) => {
for (const key in data) {
!isKey(key) ? set(value, key, data[key]) : (value[key] = data[key]);
}
return value;
};
var isUndefined = (val) => val === undefined;
var get = (obj = {}, path, defaultValue) => {
const result = compact(path.split(/[,[\].]+?/)).reduce((result, key) => (isNullOrUndefined(result) ? result : result[key]), obj);
return isUndefined(result) || result === obj
? isUndefined(obj[path])
? defaultValue
: obj[path]
: result;
};
var focusOnErrorField = (fields, fieldErrors) => {
for (const key in fields) {
if (get(fieldErrors, key)) {
const field = fields[key];
if (field) {
if (field.ref.focus && isUndefined(field.ref.focus())) {
break;
}
else if (field.options) {
field.options[0].ref.focus();
break;
}
}
}
}
};
var removeAllEventListeners = (ref, validateWithStateUpdate) => {
if (isHTMLElement(ref) && ref.removeEventListener) {
ref.removeEventListener(EVENTS.INPUT, validateWithStateUpdate);
ref.removeEventListener(EVENTS.CHANGE, validateWithStateUpdate);
ref.removeEventListener(EVENTS.BLUR, validateWithStateUpdate);
}
};
const defaultReturn = {
isValid: false,
value: null,
};
var getRadioValue = (options) => Array.isArray(options)
? options.reduce((previous, option) => option && option.ref.checked
? {
isValid: true,
value: option.ref.value,
}
: previous, defaultReturn)
: defaultReturn;
var getMultipleSelectValue = (options) => [...options]
.filter(({ selected }) => selected)
.map(({ value }) => value);
var isRadioInput = (element) => element.type === 'radio';
var isFileInput = (element) => element.type === 'file';
var isCheckBoxInput = (element) => element.type === 'checkbox';
var isMultipleSelect = (element) => element.type === `${SELECT}-multiple`;
const defaultResult = {
value: false,
isValid: false,
};
const validResult = { value: true, isValid: true };
var getCheckboxValue = (options) => {
if (Array.isArray(options)) {
if (options.length > 1) {
const values = options
.filter((option) => option && option.ref.checked)
.map(({ ref: { value } }) => value);
return { value: values, isValid: !!values.length };
}
const { checked, value, attributes } = options[0].ref;
return checked
? attributes && !isUndefined(attributes.value)
? isUndefined(value) || value === ''
? validResult
: { value: value, isValid: true }
: validResult
: defaultResult;
}
return defaultResult;
};
function getFieldValue(fieldsRef, name, shallowFieldsStateRef, excludeDisabled, shouldKeepRawValue) {
const field = fieldsRef.current[name];
if (field) {
const { ref: { value, disabled }, ref, valueAsNumber, valueAsDate, setValueAs, } = field;
if (disabled && excludeDisabled) {
return;
}
if (isFileInput(ref)) {
return ref.files;
}
if (isRadioInput(ref)) {
return getRadioValue(field.options).value;
}
if (isMultipleSelect(ref)) {
return getMultipleSelectValue(ref.options);
}
if (isCheckBoxInput(ref)) {
return getCheckboxValue(field.options).value;
}
return shouldKeepRawValue
? value
: valueAsNumber
? value === ''
? NaN
: +value
: valueAsDate
? ref.valueAsDate
: setValueAs
? setValueAs(value)
: value;
}
if (shallowFieldsStateRef) {
return get(shallowFieldsStateRef.current, name);
}
}
function isDetached(element) {
if (!element) {
return true;
}
if (!(element instanceof HTMLElement) ||
element.nodeType === Node.DOCUMENT_NODE) {
return false;
}
return isDetached(element.parentNode);
}
var isEmptyObject = (value) => isObject(value) && !Object.keys(value).length;
var isBoolean = (value) => typeof value === 'boolean';
function baseGet(object, updatePath) {
const length = updatePath.slice(0, -1).length;
let index = 0;
while (index < length) {
object = isUndefined(object) ? index++ : object[updatePath[index++]];
}
return object;
}
function unset(object, path) {
const updatePath = isKey(path) ? [path] : stringToPath(path);
const childObject = updatePath.length == 1 ? object : baseGet(object, updatePath);
const key = updatePath[updatePath.length - 1];
let previousObjRef;
if (childObject) {
delete childObject[key];
}
for (let k = 0; k < updatePath.slice(0, -1).length; k++) {
let index = -1;
let objectRef;
const currentPaths = updatePath.slice(0, -(k + 1));
const currentPathsLength = currentPaths.length - 1;
if (k > 0) {
previousObjRef = object;
}
while (++index < currentPaths.length) {
const item = currentPaths[index];
objectRef = objectRef ? objectRef[item] : object[item];
if (currentPathsLength === index &&
((isObject(objectRef) && isEmptyObject(objectRef)) ||
(Array.isArray(objectRef) &&
!objectRef.filter((data) => (isObject(data) && !isEmptyObject(data)) || isBoolean(data)).length))) {
previousObjRef ? delete previousObjRef[item] : delete object[item];
}
previousObjRef = objectRef;
}
}
return object;
}
const isSameRef = (fieldValue, ref) => fieldValue && fieldValue.ref === ref;
function findRemovedFieldAndRemoveListener(fieldsRef, handleChange, field, shallowFieldsStateRef, shouldUnregister, forceDelete) {
const { ref, ref: { name }, } = field;
const fieldRef = fieldsRef.current[name];
if (!shouldUnregister) {
const value = getFieldValue(fieldsRef, name, shallowFieldsStateRef);
!isUndefined(value) && set(shallowFieldsStateRef.current, name, value);
}
if (!ref.type || !fieldRef) {
delete fieldsRef.current[name];
return;
}
if (isRadioInput(ref) || isCheckBoxInput(ref)) {
if (Array.isArray(fieldRef.options) && fieldRef.options.length) {
compact(fieldRef.options).forEach((option = {}, index) => {
if ((isDetached(option.ref) && isSameRef(option, option.ref)) ||
forceDelete) {
removeAllEventListeners(option.ref, handleChange);
unset(fieldRef.options, `[${index}]`);
}
});
if (fieldRef.options && !compact(fieldRef.options).length) {
delete fieldsRef.current[name];
}
}
else {
delete fieldsRef.current[name];
}
}
else if ((isDetached(ref) && isSameRef(fieldRef, ref)) || forceDelete) {
removeAllEventListeners(ref, handleChange);
delete fieldsRef.current[name];
}
}
var isPrimitive = (value) => isNullOrUndefined(value) || !isObjectType(value);
function deepMerge(target, source) {
if (isPrimitive(target) || isPrimitive(source)) {
return source;
}
for (const key in source) {
const targetValue = target[key];
const sourceValue = source[key];
try {
target[key] =
(isObject(targetValue) && isObject(sourceValue)) ||
(Array.isArray(targetValue) && Array.isArray(sourceValue))
? deepMerge(targetValue, sourceValue)
: sourceValue;
}
catch (_a) { }
}
return target;
}
function deepEqual(object1, object2, isErrorObject) {
if (isPrimitive(object1) ||
isPrimitive(object2) ||
object1 instanceof Date ||
object2 instanceof Date) {
return object1 === object2;
}
if (!React.isValidElement(object1)) {
const keys1 = Object.keys(object1);
const keys2 = Object.keys(object2);
if (keys1.length !== keys2.length) {
return false;
}
for (const key of keys1) {
const val1 = object1[key];
if (!(isErrorObject && key === 'ref')) {
const val2 = object2[key];
if ((isObject(val1) || Array.isArray(val1)) &&
(isObject(val2) || Array.isArray(val2))
? !deepEqual(val1, val2, isErrorObject)
: val1 !== val2) {
return false;
}
}
}
}
return true;
}
function setDirtyFields(values, defaultValues, dirtyFields, parentNode, parentName) {
let index = -1;
while (++index < values.length) {
for (const key in values[index]) {
if (Array.isArray(values[index][key])) {
!dirtyFields[index] && (dirtyFields[index] = {});
dirtyFields[index][key] = [];
setDirtyFields(values[index][key], get(defaultValues[index] || {}, key, []), dirtyFields[index][key], dirtyFields[index], key);
}
else {
deepEqual(get(defaultValues[index] || {}, key), values[index][key])
? set(dirtyFields[index] || {}, key)
: (dirtyFields[index] = Object.assign(Object.assign({}, dirtyFields[index]), { [key]: true }));
}
}
parentNode &&
!dirtyFields.length &&
delete parentNode[parentName];
}
return dirtyFields;
}
var setFieldArrayDirtyFields = (values, defaultValues, dirtyFields) => deepMerge(setDirtyFields(values, defaultValues, dirtyFields.slice(0, values.length)), setDirtyFields(defaultValues, values, dirtyFields.slice(0, values.length)));
var isString = (value) => typeof value === 'string';
var getFieldsValues = (fieldsRef, shallowFieldsState, shouldUnregister, excludeDisabled, search) => {
const output = {};
for (const name in fieldsRef.current) {
if (isUndefined(search) ||
(isString(search)
? name.startsWith(search)
: Array.isArray(search) && search.find((data) => name.startsWith(data)))) {
output[name] = getFieldValue(fieldsRef, name, undefined, excludeDisabled);
}
}
return shouldUnregister
? transformToNestObject(output)
: deepMerge(shallowFieldsState, transformToNestObject(output));
};
var isErrorStateChanged = ({ errors, name, error, validFields, fieldsWithValidation, }) => {
const isValid = isUndefined(error);
const previousError = get(errors, name);
return ((isValid && !!previousError) ||
(!isValid && !deepEqual(previousError, error, true)) ||
(isValid && get(fieldsWithValidation, name) && !get(validFields, name)));
};
var isRegex = (value) => value instanceof RegExp;
var getValueAndMessage = (validationData) => isObject(validationData) && !isRegex(validationData)
? validationData
: {
value: validationData,
message: '',
};
var isFunction = (value) => typeof value === 'function';
var isMessage = (value) => isString(value) || React.isValidElement(value);
function getValidateError(result, ref, type = 'validate') {
if (isMessage(result) || (isBoolean(result) && !result)) {
return {
type,
message: isMessage(result) ? result : '',
ref,
};
}
}
var appendErrors = (name, validateAllFieldCriteria, errors, type, message) => validateAllFieldCriteria
? Object.assign(Object.assign({}, errors[name]), { types: Object.assign(Object.assign({}, (errors[name] && errors[name].types ? errors[name].types : {})), { [type]: message || true }) }) : {};
var validateField = async (fieldsRef, validateAllFieldCriteria, { ref, ref: { value }, options, required, maxLength, minLength, min, max, pattern, validate, }, shallowFieldsStateRef) => {
const name = ref.name;
const error = {};
const isRadio = isRadioInput(ref);
const isCheckBox = isCheckBoxInput(ref);
const isRadioOrCheckbox = isRadio || isCheckBox;
const isEmpty = value === '';
const appendErrorsCurry = appendErrors.bind(null, name, validateAllFieldCriteria, error);
const getMinMaxMessage = (exceedMax, maxLengthMessage, minLengthMessage, maxType = INPUT_VALIDATION_RULES.maxLength, minType = INPUT_VALIDATION_RULES.minLength) => {
const message = exceedMax ? maxLengthMessage : minLengthMessage;
error[name] = Object.assign({ type: exceedMax ? maxType : minType, message,
ref }, (exceedMax
? appendErrorsCurry(maxType, message)
: appendErrorsCurry(minType, message)));
};
if (required &&
((!isRadio && !isCheckBox && (isEmpty || isNullOrUndefined(value))) ||
(isBoolean(value) && !value) ||
(isCheckBox && !getCheckboxValue(options).isValid) ||
(isRadio && !getRadioValue(options).isValid))) {
const { value, message } = isMessage(required)
? { value: !!required, message: required }
: getValueAndMessage(required);
if (value) {
error[name] = Object.assign({ type: INPUT_VALIDATION_RULES.required, message, ref: isRadioOrCheckbox
? ((fieldsRef.current[name].options || [])[0] || {}).ref
: ref }, appendErrorsCurry(INPUT_VALIDATION_RULES.required, message));
if (!validateAllFieldCriteria) {
return error;
}
}
}
if ((!isNullOrUndefined(min) || !isNullOrUndefined(max)) && value !== '') {
let exceedMax;
let exceedMin;
const maxOutput = getValueAndMessage(max);
const minOutput = getValueAndMessage(min);
if (!isNaN(value)) {
const valueNumber = ref.valueAsNumber || parseFloat(value);
if (!isNullOrUndefined(maxOutput.value)) {
exceedMax = valueNumber > maxOutput.value;
}
if (!isNullOrUndefined(minOutput.value)) {
exceedMin = valueNumber < minOutput.value;
}
}
else {
const valueDate = ref.valueAsDate || new Date(value);
if (isString(maxOutput.value)) {
exceedMax = valueDate > new Date(maxOutput.value);
}
if (isString(minOutput.value)) {
exceedMin = valueDate < new Date(minOutput.value);
}
}
if (exceedMax || exceedMin) {
getMinMaxMessage(!!exceedMax, maxOutput.message, minOutput.message, INPUT_VALIDATION_RULES.max, INPUT_VALIDATION_RULES.min);
if (!validateAllFieldCriteria) {
return error;
}
}
}
if (isString(value) && !isEmpty && (maxLength || minLength)) {
const maxLengthOutput = getValueAndMessage(maxLength);
const minLengthOutput = getValueAndMessage(minLength);
const exceedMax = !isNullOrUndefined(maxLengthOutput.value) &&
value.length > maxLengthOutput.value;
const exceedMin = !isNullOrUndefined(minLengthOutput.value) &&
value.length < minLengthOutput.value;
if (exceedMax || exceedMin) {
getMinMaxMessage(exceedMax, maxLengthOutput.message, minLengthOutput.message);
if (!validateAllFieldCriteria) {
return error;
}
}
}
if (isString(value) && pattern && !isEmpty) {
const { value: patternValue, message } = getValueAndMessage(pattern);
if (isRegex(patternValue) && !patternValue.test(value)) {
error[name] = Object.assign({ type: INPUT_VALIDATION_RULES.pattern, message,
ref }, appendErrorsCurry(INPUT_VALIDATION_RULES.pattern, message));
if (!validateAllFieldCriteria) {
return error;
}
}
}
if (validate) {
const fieldValue = getFieldValue(fieldsRef, name, shallowFieldsStateRef, false, true);
const validateRef = isRadioOrCheckbox && options ? options[0].ref : ref;
if (isFunction(validate)) {
const result = await validate(fieldValue);
const validateError = getValidateError(result, validateRef);
if (validateError) {
error[name] = Object.assign(Object.assign({}, validateError), appendErrorsCurry(INPUT_VALIDATION_RULES.validate, validateError.message));
if (!validateAllFieldCriteria) {
return error;
}
}
}
else if (isObject(validate)) {
let validationResult = {};
for (const [key, validateFunction] of Object.entries(validate)) {
if (!isEmptyObject(validationResult) && !validateAllFieldCriteria) {
break;
}
const validateResult = await validateFunction(fieldValue);
const validateError = getValidateError(validateResult, validateRef, key);
if (validateError) {
validationResult = Object.assign(Object.assign({}, validateError), appendErrorsCurry(key, validateError.message));
if (validateAllFieldCriteria) {
error[name] = validationResult;
}
}
}
if (!isEmptyObject(validationResult)) {
error[name] = Object.assign({ ref: validateRef }, validationResult);
if (!validateAllFieldCriteria) {
return error;
}
}
}
}
return error;
};
const getPath = (rootPath, values, paths = []) => {
for (const property in values) {
const rootName = (rootPath +
(isObject(values)
? `.${property}`
: `[${property}]`));
isPrimitive(values[property])
? paths.push(rootName)
: getPath(rootName, values[property], paths);
}
return paths;
};
var assignWatchFields = (fieldValues, fieldName, watchFields, inputValue, isSingleField) => {
let value = undefined;
watchFields.add(fieldName);
if (!isEmptyObject(fieldValues)) {
value = get(fieldValues, fieldName);
if (isObject(value) || Array.isArray(value)) {
getPath(fieldName, value).forEach((name) => watchFields.add(name));
}
}
return isUndefined(value)
? isSingleField
? inputValue
: get(inputValue, fieldName)
: value;
};
var skipValidation = ({ isOnBlur, isOnChange, isOnTouch, isTouched, isReValidateOnBlur, isReValidateOnChange, isBlurEvent, isSubmitted, isOnAll, }) => {
if (isOnAll) {
return false;
}
else if (!isSubmitted && isOnTouch) {
return !(isTouched || isBlurEvent);
}
else if (isSubmitted ? isReValidateOnBlur : isOnBlur) {
return !isBlurEvent;
}
else if (isSubmitted ? isReValidateOnChange : isOnChange) {
return isBlurEvent;
}
return true;
};
var getFieldArrayParentName = (name) => name.substring(0, name.indexOf('['));
const isMatchFieldArrayName = (name, searchName) => RegExp(`^${searchName}([|.)\\d+`.replace(/\[/g, '\\[').replace(/\]/g, '\\]')).test(name);
var isNameInFieldArray = (names, name) => [...names].some((current) => isMatchFieldArrayName(name, current));
var isSelectInput = (element) => element.type === `${SELECT}-one`;
function onDomRemove(fieldsRef, removeFieldEventListenerAndRef) {
const observer = new MutationObserver(() => {
for (const field of Object.values(fieldsRef.current)) {
if (field && field.options) {
for (const option of field.options) {
if (option && option.ref && isDetached(option.ref)) {
removeFieldEventListenerAndRef(field);
}
}
}
else if (field && isDetached(field.ref)) {
removeFieldEventListenerAndRef(field);
}
}
});
observer.observe(window.document, {
childList: true,
subtree: true,
});
return observer;
}
var isWeb = typeof window !== UNDEFINED && typeof document !== UNDEFINED;
function cloneObject(data) {
var _a;
let copy;
if (isPrimitive(data) ||
(isWeb && (data instanceof File || isHTMLElement(data)))) {
return data;
}
if (!['Set', 'Map', 'Object', 'Date', 'Array'].includes((_a = data.constructor) === null || _a === void 0 ? void 0 : _a.name)) {
return data;
}
if (data instanceof Date) {
copy = new Date(data.getTime());
return copy;
}
if (data instanceof Set) {
copy = new Set();
for (const item of data) {
copy.add(item);
}
return copy;
}
if (data instanceof Map) {
copy = new Map();
for (const key of data.keys()) {
copy.set(key, cloneObject(data.get(key)));
}
return copy;
}
copy = Array.isArray(data) ? [] : {};
for (const key in data) {
copy[key] = cloneObject(data[key]);
}
return copy;
}
var modeChecker = (mode) => ({
isOnSubmit: !mode || mode === VALIDATION_MODE.onSubmit,
isOnBlur: mode === VALIDATION_MODE.onBlur,
isOnChange: mode === VALIDATION_MODE.onChange,
isOnAll: mode === VALIDATION_MODE.all,
isOnTouch: mode === VALIDATION_MODE.onTouched,
});
var isRadioOrCheckboxFunction = (ref) => isRadioInput(ref) || isCheckBoxInput(ref);
const isWindowUndefined = typeof window === UNDEFINED;
const isProxyEnabled = isWeb ? 'Proxy' in window : typeof Proxy !== UNDEFINED;
function useForm({ mode = VALIDATION_MODE.onSubmit, reValidateMode = VALIDATION_MODE.onChange, resolver, context, defaultValues = {}, shouldFocusError = true, shouldUnregister = true, criteriaMode, } = {}) {
const fieldsRef = React.useRef({});
const fieldArrayDefaultValuesRef = React.useRef({});
const fieldArrayValuesRef = React.useRef({});
const watchFieldsRef = React.useRef(new Set());
const useWatchFieldsRef = React.useRef({});
const useWatchRenderFunctionsRef = React.useRef({});
const fieldsWithValidationRef = React.useRef({});
const validFieldsRef = React.useRef({});
const defaultValuesRef = React.useRef(defaultValues);
const isUnMount = React.useRef(false);
const isWatchAllRef = React.useRef(false);
const handleChangeRef = React.useRef();
const shallowFieldsStateRef = React.useRef({});
const resetFieldArrayFunctionRef = React.useRef({});
const contextRef = React.useRef(context);
const resolverRef = React.useRef(resolver);
const fieldArrayNamesRef = React.useRef(new Set());
const modeRef = React.useRef(modeChecker(mode));
const { isOnSubmit, isOnTouch } = modeRef.current;
const isValidateAllFieldCriteria = criteriaMode === VALIDATION_MODE.all;
const [formState, setFormState] = React.useState({
isDirty: false,
isValidating: false,
dirtyFields: {},
isSubmitted: false,
submitCount: 0,
touched: {},
isSubmitting: false,
isSubmitSuccessful: false,
isValid: !isOnSubmit,
errors: {},
});
const readFormStateRef = React.useRef({
isDirty: !isProxyEnabled,
dirtyFields: !isProxyEnabled,
touched: !isProxyEnabled || isOnTouch,
isValidating: !isProxyEnabled,
isSubmitting: !isProxyEnabled,
isValid: !isProxyEnabled,
});
const formStateRef = React.useRef(formState);
const observerRef = React.useRef();
const { isOnBlur: isReValidateOnBlur, isOnChange: isReValidateOnChange, } = React.useRef(modeChecker(reValidateMode)).current;
contextRef.current = context;
resolverRef.current = resolver;
formStateRef.current = formState;
shallowFieldsStateRef.current = shouldUnregister
? {}
: isEmptyObject(shallowFieldsStateRef.current)
? cloneObject(defaultValues)
: shallowFieldsStateRef.current;
const updateFormState = React.useCallback((state = {}) => {
if (!isUnMount.current) {
formStateRef.current = Object.assign(Object.assign({}, formStateRef.current), state);
setFormState(formStateRef.current);
}
}, []);
const updateIsValidating = () => readFormStateRef.current.isValidating &&
updateFormState({
isValidating: true,
});
const shouldRenderBaseOnError = React.useCallback((name, error, shouldRender = false, state = {}, isValid) => {
let shouldReRender = shouldRender ||
isErrorStateChanged({
errors: formStateRef.current.errors,
error,
name,
validFields: validFieldsRef.current,
fieldsWithValidation: fieldsWithValidationRef.current,
});
const previousError = get(formStateRef.current.errors, name);
if (error) {
unset(validFieldsRef.current, name);
shouldReRender =
shouldReRender ||
!previousError ||
!deepEqual(previousError, error, true);
set(formStateRef.current.errors, name, error);
}
else {
if (get(fieldsWithValidationRef.current, name) || resolverRef.current) {
set(validFieldsRef.current, name, true);
shouldReRender = shouldReRender || previousError;
}
unset(formStateRef.current.errors, name);
}
if ((shouldReRender && !isNullOrUndefined(shouldRender)) ||
!isEmptyObject(state) ||
readFormStateRef.current.isValidating) {
updateFormState(Object.assign(Object.assign(Object.assign({}, state), (resolverRef.current ? { isValid: !!isValid } : {})), { isValidating: false }));
}
}, []);
const setFieldValue = React.useCallback((name, rawValue) => {
const { ref, options } = fieldsRef.current[name];
const value = isWeb && isHTMLElement(ref) && isNullOrUndefined(rawValue)
? ''
: rawValue;
if (isRadioInput(ref)) {
(options || []).forEach(({ ref: radioRef }) => (radioRef.checked = radioRef.value === value));
}
else if (isFileInput(ref) && !isString(value)) {
ref.files = value;
}
else if (isMultipleSelect(ref)) {
[...ref.options].forEach((selectRef) => (selectRef.selected = value.includes(selectRef.value)));
}
else if (isCheckBoxInput(ref) && options) {
options.length > 1
? options.forEach(({ ref: checkboxRef }) => (checkboxRef.checked = Array.isArray(value)
? !!value.find((data) => data === checkboxRef.value)
: value === checkboxRef.value))
: (options[0].ref.checked = !!value);
}
else {
ref.value = value;
}
}, []);
const isFormDirty = React.useCallback((name, data) => {
if (readFormStateRef.current.isDirty) {
const formValues = getValues();
name && data && set(formValues, name, data);
return !deepEqual(formValues, defaultValuesRef.current);
}
return false;
}, []);
const updateAndGetDirtyState = React.useCallback((name, shouldRender = true) => {
if (readFormStateRef.current.isDirty ||
readFormStateRef.current.dirtyFields) {
const isFieldDirty = !deepEqual(get(defaultValuesRef.current, name), getFieldValue(fieldsRef, name, shallowFieldsStateRef));
const isDirtyFieldExist = get(formStateRef.current.dirtyFields, name);
const previousIsDirty = formStateRef.current.isDirty;
isFieldDirty
? set(formStateRef.current.dirtyFields, name, true)
: unset(formStateRef.current.dirtyFields, name);
const state = {
isDirty: isFormDirty(),
dirtyFields: formStateRef.current.dirtyFields,
};
const isChanged = (readFormStateRef.current.isDirty &&
previousIsDirty !== state.isDirty) ||
(readFormStateRef.current.dirtyFields &&
isDirtyFieldExist !== get(formStateRef.current.dirtyFields, name));
isChanged && shouldRender && updateFormState(state);
return isChanged ? state : {};
}
return {};
}, []);
const executeValidation = React.useCallback(async (name, skipReRender) => {
{
if (!fieldsRef.current[name]) {
console.warn('📋 Field is missing with `name` attribute: ', name);
return false;
}
}
const error = (await validateField(fieldsRef, isValidateAllFieldCriteria, fieldsRef.current[name], shallowFieldsStateRef))[name];
shouldRenderBaseOnError(name, error, skipReRender);
return isUndefined(error);
}, [shouldRenderBaseOnError, isValidateAllFieldCriteria]);
const executeSchemaOrResolverValidation = React.useCallback(async (names) => {
const { errors } = await resolverRef.current(getValues(), contextRef.current, isValidateAllFieldCriteria);
const previousFormIsValid = formStateRef.current.isValid;
if (Array.isArray(names)) {
const isInputsValid = names
.map((name) => {
const error = get(errors, name);
error
? set(formStateRef.current.errors, name, error)
: unset(formStateRef.current.errors, name);
return !error;
})
.every(Boolean);
updateFormState({
isValid: isEmptyObject(errors),
isValidating: false,
});
return isInputsValid;
}
else {
const error = get(errors, names);
shouldRenderBaseOnError(names, error, previousFormIsValid !== isEmptyObject(errors), {}, isEmptyObject(errors));
return !error;
}
}, [shouldRenderBaseOnError, isValidateAllFieldCriteria]);
const trigger = React.useCallback(async (name) => {
const fields = name || Object.keys(fieldsRef.current);
updateIsValidating();
if (resolverRef.current) {
return executeSchemaOrResolverValidation(fields);
}
if (Array.isArray(fields)) {
!name && (formStateRef.current.errors = {});
const result = await Promise.all(fields.map(async (data) => await executeValidation(data, null)));
updateFormState({
isValidating: false,
});
return result.every(Boolean);
}
return await executeValidation(fields);
}, [executeSchemaOrResolverValidation, executeValidation]);
const setInternalValues = React.useCallback((name, value, { shouldDirty, shouldValidate }) => {
const data = {};
set(data, name, value);
for (const fieldName of getPath(name, value)) {
if (fieldsRef.current[fieldName]) {
setFieldValue(fieldName, get(data, fieldName));
shouldDirty && updateAndGetDirtyState(fieldName);
shouldValidate && trigger(fieldName);
}
}
}, [trigger, setFieldValue, updateAndGetDirtyState]);
const setInternalValue = React.useCallback((name, value, config) => {
!shouldUnregister &&
!isPrimitive(value) &&
set(shallowFieldsStateRef.current, name, Array.isArray(value) ? [...value] : Object.assign({}, value));
if (fieldsRef.current[name]) {
setFieldValue(name, value);
config.shouldDirty && updateAndGetDirtyState(name);
config.shouldValidate && trigger(name);
}
else if (!isPrimitive(value)) {
setInternalValues(name, value, config);
if (fieldArrayNamesRef.current.has(name)) {
const parentName = getFieldArrayParentName(name) || name;
set(fieldArrayDefaultValuesRef.current, name, value);
resetFieldArrayFunctionRef.current[parentName]({
[parentName]: get(fieldArrayDefaultValuesRef.current, parentName),
});
if ((readFormStateRef.current.isDirty ||
readFormStateRef.current.dirtyFields) &&
config.shouldDirty) {
set(formStateRef.current.dirtyFields, name, setFieldArrayDirtyFields(value, get(defaultValuesRef.current, name, []), get(formStateRef.current.dirtyFields, name, [])));
updateFormState({
isDirty: !deepEqual(Object.assign(Object.assign({}, getValues()), { [name]: value }), defaultValuesRef.current),
});
}
}
}
!shouldUnregister && set(shallowFieldsStateRef.current, name, value);
}, [updateAndGetDirtyState, setFieldValue, setInternalValues]);
const isFieldWatched = (name) => isWatchAllRef.current ||
watchFieldsRef.current.has(name) ||
watchFieldsRef.current.has((name.match(/\w+/) || [])[0]);
const renderWatchedInputs = (name) => {
let found = true;
if (!isEmptyObject(useWatchFieldsRef.current)) {
for (const key in useWatchFieldsRef.current) {
if (!name ||
!useWatchFieldsRef.current[key].size ||
useWatchFieldsRef.current[key].has(name) ||
useWatchFieldsRef.current[key].has(getFieldArrayParentName(name))) {
useWatchRenderFunctionsRef.current[key]();
found = false;
}
}
}
return found;
};
function setValue(name, value, config) {
setInternalValue(name, value, config || {});
isFieldWatched(name) && updateFormState();
renderWatchedInputs(name);
}
handleChangeRef.current = handleChangeRef.current
? handleChangeRef.current
: async ({ type, target }) => {
let name = target.name;
const field = fieldsRef.current[name];
let error;
let isValid;
if (field) {
const isBlurEvent = type === EVENTS.BLUR;
const shouldSkipValidation = skipValidation(Object.assign({ isBlurEvent,
isReValidateOnChange,
isReValidateOnBlur, isTouched: !!get(formStateRef.current.touched, name), isSubmitted: formStateRef.current.isSubmitted }, modeRef.current));
let state = updateAndGetDirtyState(name, false);
let shouldRender = !isEmptyObject(state) ||
(!isBlurEvent && isFieldWatched(name));
if (isBlurEvent &&
!get(formStateRef.current.touched, name) &&
readFormStateRef.current.touched) {
set(formStateRef.current.touched, name, true);
state = Object.assign(Object.assign({}, state), { touched: formStateRef.current.touched });
}
if (!shouldUnregister && isCheckBoxInput(target)) {
set(shallowFieldsStateRef.current, name, getFieldValue(fieldsRef, name));
}
if (shouldSkipValidation) {
!isBlurEvent && renderWatchedInputs(name);
return ((!isEmptyObject(state) ||
(shouldRender && isEmptyObject(state))) &&
updateFormState(state));
}
updateIsValidating();
if (resolverRef.current) {
const { errors } = await resolverRef.current(getValues(), contextRef.current, isValidateAllFieldCriteria);
const previousFormIsValid = formStateRef.current.isValid;
error = get(errors, name);
if (isCheckBoxInput(target) &&
!error &&
resolverRef.current) {
const parentNodeName = getFieldArrayParentName(name);
const currentError = get(errors, parentNodeName, {});
currentError.type &&
currentError.message &&
(error = currentError);
if (parentNodeName &&
(currentError ||
get(formStateRef.current.errors, parentNodeName))) {
name = parentNodeName;
}
}
isValid = isEmptyObject(errors);
previousFormIsValid !== isValid && (shouldRender = true);
}
else {
error = (await validateField(fieldsRef, isValidateAllFieldCriteria, field, shallowFieldsStateRef))[name];
}
!isBlurEvent && renderWatchedInputs(name);
shouldRenderBaseOnError(name, error, shouldRender, state, isValid);
}
};
function setFieldArrayDefaultValues(data) {
if (!shouldUnregister) {
let copy = cloneObject(data);
for (const value of fieldArrayNamesRef.current) {
if (isKey(value) && !copy[value]) {
copy = Object.assign(Object.assign({}, copy), { [value]: [] });
}
}
return copy;
}
return data;
}
function getValues(payload) {
if (isString(payload)) {
return getFieldValue(fieldsRef, payload, shallowFieldsStateRef);
}
if (Array.isArray(payload)) {
const data = {};
for (const name of payload) {
set(data, name, getFieldValue(fieldsRef, name, shallowFieldsStateRef));
}
return data;
}
return setFieldArrayDefaultValues(getFieldsValues(fieldsRef, cloneObject(shallowFieldsStateRef.current), shouldUnregister));
}
const validateResolver = React.useCallback(async (values = {}) => {
const newDefaultValues = isEmptyObject(fieldsRef.current)
? defaultValuesRef.current
: {};
const { errors } = (await resolverRef.current(Object.assign(Object.assign(Object.assign({}, newDefaultValues), getValues()), values), contextRef.current, isValidateAllFieldCriteria)) || {};
const isValid = isEmptyObject(errors);
formStateRef.current.isValid !== isValid &&
updateFormState({
isValid,
});
}, [isValidateAllFieldCriteria]);
const removeFieldEventListener = React.useCallback((field, forceDelete) => {
findRemovedFieldAndRemoveListener(fieldsRef, handleChangeRef.current, field, shallowFieldsStateRef, shouldUnregister, forceDelete);
if (shouldUnregister) {
unset(validFieldsRef.current, field.ref.name);
unset(fieldsWithValidationRef.current, field.ref.name);
}
}, [shouldUnregister]);
const updateWatchedValue = React.useCallback((name) => {
if (isWatchAllRef.current) {
updateFormState();
}
else {
for (const watchField of watchFieldsRef.current) {
if (watchField.startsWith(name)) {
updateFormState();
break;
}
}
renderWatchedInputs(name);
}
}, []);
const removeFieldEventListenerAndRef = React.useCallback((field, forceDelete) => {
if (field) {
removeFieldEventListener(field, forceDelete);
if (shouldUnregister && !compact(field.options || []).length) {
unset(formStateRef.current.errors, field.ref.name);
set(formStateRef.current.dirtyFields, field.ref.name, true);
updateFormState({
isDirty: isFormDirty(),
});
readFormStateRef.current.isValid &&
resolverRef.current &&
validateResolver();
updateWatchedValue(field.ref.name);
}
}
}, [validateResolver, removeFieldEventListener]);
function clearErrors(name) {
name &&
(Array.isArray(name) ? name : [name]).forEach((inputName) => fieldsRef.current[inputName] && isKey(inputName)
? delete formStateRef.current.errors[inputName]
: unset(formStateRef.current.errors, inputName));
updateFormState({
errors: name ? formStateRef.current.errors : {},
});
}
function setError(name, error) {
const ref = (fieldsRef.current[name] || {}).ref;
set(formStateRef.current.errors, name, Object.assign(Object.assign({}, error), { ref }));
updateFormState({
isValid: false,
});
error.shouldFocus && ref && ref.focus && ref.focus();
}
const watchInternal = React.useCallback((fieldNames, defaultValue, watchId) => {
const watchFields = watchId
? useWatchFieldsRef.current[watchId]
: watchFieldsRef.current;
let fieldValues = getFieldsValues(fieldsRef, cloneObject(shallowFieldsStateRef.current), shouldUnregister, false, fieldNames);
if (isString(fieldNames)) {
const parentNodeName = getFieldArrayParentName(fieldNames) || fieldNames;
if (fieldArrayNamesRef.current.has(parentNodeName)) {
fieldValues = Object.assign(Object.assign({}, fieldArrayValuesRef.current), fieldValues);
}
return assignWatchFields(fieldValues, fieldNames, watchFields, isUndefined(get(defaultValuesRef.current, fieldNames))
? defaultValue
: get(defaultValuesRef.current, fieldNames), true);
}
const combinedDefaultValues = isUndefined(defaultValue)
? defaultValuesRef.current
: defaultValue;
if (Array.isArray(fieldNames)) {
return fieldNames.reduce((previous, name) => (Object.assign(Object.assign({}, previous), { [name]: assignWatchFields(fieldValues, name, watchFields, combinedDefaultValues) })), {});
}
isWatchAllRef.current = isUndefined(watchId);
return transformToNestObject((!isEmptyObject(fieldValues) && fieldValues) ||
combinedDefaultValues);
}, []);
function watch(fieldNames, defaultValue) {
return watchInternal(fieldNames, defaultValue);
}
function unregister(name) {
for (const fieldName of Array.isArray(name) ? name : [name]) {
removeFieldEventListenerAndRef(fieldsRef.current[fieldName], true);
}
}
function registerFieldRef(ref, options = {}) {
{
if (!ref.name) {
return console.warn('📋 Field is missing `name` attribute', ref, `https://react-hook-form.com/api#useForm`);
}
if (fieldArrayNamesRef.current.has(ref.name.split(/\[\d+\]$/)[0]) &&
!RegExp(`^${ref.name.split(/\[\d+\]$/)[0]}[\\d+].\\w+`
.replace(/\[/g, '\\[')
.replace(/\]/g, '\\]')).test(ref.name)) {
return console.warn('📋 `name` prop should be in object shape: name="test[index].name"', ref, 'https://react-hook-form.com/api#useFieldArray');
}
}
const { name, type, value } = ref;
const fieldRefAndValidationOptions = Object.assign({ ref }, options);
const fields = fieldsRef.current;
const isRadioOrCheckbox = isRadioOrCheckboxFunction(ref);
const isFieldArray = isNameInFieldArray(fieldArrayNamesRef.current, name);
const compareRef = (currentRef) => isWeb && (!isHTMLElement(ref) || currentRef === ref);
let field = fields[name];
let isEmptyDefaultValue = true;
let defaultValue;
if (field &&
(isRadioOrCheckbox
? Array.isArray(field.options) &&
compact(field.options).find((option) => {
return value === option.ref.value && compareRef(option.ref);
})
: compareRef(field.ref))) {
fields[name] = Object.assign(Object.assign({}, field), options);
return;
}
if (type) {
field = isRadioOrCheckbox
? Object.assign({ options: [
...compact((field && field.options) || []),
{
ref,
},
], ref: { type, name } }, options) : Object.assign({}, fieldRefAndValidationOptions);
}
else {
field = fieldRefAndValidationOptions;
}
fields[name] = field;
const isEmptyUnmountFields = isUndefined(get(shallowFieldsStateRef.current, name));
if (!isEmptyObject(defaultValuesRef.current) || !isEmptyUnmountFields) {
defaultValue = get(isEmptyUnmountFields
? defaultValuesRef.current
: shallowFieldsStateRef.current, name);
isEmptyDefaultValue = isUndefined(defaultValue);
if (!isEmptyDefaultValue && !isFieldArray) {
setFieldValue(name, defaultValue);
}
}
if (!isEmptyObject(options)) {
set(fieldsWithValidationRef.current, name, true);
if (!isOnSubmit && readFormStateRef.current.isValid) {
validateField(fieldsRef, isValidateAllFieldCriteria, field, shallowFieldsStateRef).then((error) => {
const previousFormIsValid = formStateRef.current.isValid;
isEmptyObject(error)
? set(validFieldsRef.current, name, true)
: unset(validFieldsRef.current, name);
previousFormIsValid !== isEmptyObject(error) && updateFormState();
});
}
}
if (shouldUnregister && !(isFieldArray && isEmptyDefaultValue)) {
!isFieldArray && unset(formStateRef.current.dirtyFields, name);
}
if (type) {