react-hook-form
Advanced tools
Comparing version 2.0.3 to 2.1.0-beta.1
@@ -45,2 +45,3 @@ declare type Validate = (data: string | number) => boolean | string | number | Date; | ||
mutationWatcher?: any; | ||
fields?: Array<any>; | ||
options?: Array<{ | ||
@@ -47,0 +48,0 @@ ref: any; |
@@ -40,3 +40,6 @@ import { useRef, useState, useEffect } from 'react'; | ||
function getFieldsValues(fields, filedNames) { | ||
return Object.values(fields).reduce((previous, { ref, ref: { name } }) => { | ||
return Object.values(fields).reduce((previous, data) => { | ||
if (Array.isArray(data)) | ||
return; | ||
const { ref, ref: { name } } = data; | ||
const value = getFieldValue(fields, ref); | ||
@@ -82,2 +85,3 @@ if (typeof filedNames === 'string') { | ||
message: required, | ||
// @ts-ignore | ||
ref: isRadio ? fields[name].options[0].ref : ref, | ||
@@ -139,3 +143,3 @@ }; | ||
if (typeof result !== 'boolean' || !result) { | ||
copy[name] = Object.assign({}, copy[name], { type: 'validate', message: result || true, ref: isRadio ? options[0].ref : ref }); | ||
copy[name] = Object.assign({}, copy[name], { type: 'validate', message: result || true, ref: isRadio && options ? options[0].ref : ref }); | ||
return copy; | ||
@@ -164,3 +168,3 @@ } | ||
if (Object.keys(result).length) { | ||
copy[name] = Object.assign({}, copy[name], { ref: isRadio ? options[0].ref : ref }, result); | ||
copy[name] = Object.assign({}, copy[name], { ref: isRadio && options ? options[0].ref : ref }, result); | ||
return copy; | ||
@@ -226,5 +230,5 @@ } | ||
function attachEventListeners({ mode, allFields, watchFields, radioOptionIndex, ref, type, name, validateWithStateUpdate, }) { | ||
const field = allFields[name]; | ||
const isOnChange = mode === 'onChange' || watchFields.current[ref.name]; | ||
function attachEventListeners({ mode, fields, watchFields, radioOptionIndex, ref, type, name, validateWithStateUpdate, }) { | ||
const field = fields[name]; | ||
const isOnChange = mode === 'onChange' || watchFields[ref.name]; | ||
const isOnBlur = mode === 'onBlur'; | ||
@@ -306,21 +310,77 @@ if (!field || (!isOnChange && !isOnBlur)) | ||
async function validateAllFields({ previous, data, index, fieldsLength, resolve, allFields, removeReferenceAndEventListeners, validateWithStateUpdate, }) { | ||
const resolvedPrevious = await previous; | ||
const { ref, ref: { name, type }, options, } = data; | ||
const lastChild = fieldsLength - 1 === index; | ||
removeReferenceAndEventListeners(data); | ||
if (!allFields[name]) | ||
return lastChild ? resolve(resolvedPrevious) : resolvedPrevious; | ||
const fieldError = await validateField(data, allFields); | ||
const hasError = fieldError && fieldError[name]; | ||
if (!hasError) { | ||
resolvedPrevious.values[name] = getFieldValue(allFields, ref); | ||
return lastChild ? resolve(resolvedPrevious) : resolvedPrevious; | ||
} | ||
if (isRadioInput(type) && Array.isArray(options)) { | ||
options.forEach(option => { | ||
if (option.eventAttached && option.eventAttached.includes('change')) | ||
return; | ||
option.ref.addEventListener('change', validateWithStateUpdate); | ||
option.eventAttached = [...(option.eventAttached || []), 'change']; | ||
}); | ||
// @ts-ignore | ||
} | ||
else if (!data.eventAttached || !data.eventAttached.includes('input')) { | ||
ref.addEventListener('input', validateWithStateUpdate); | ||
data.eventAttached = [...(data.eventAttached || []), 'input']; | ||
} | ||
resolvedPrevious.errors = Object.assign({}, (resolvedPrevious.errors || []), fieldError); | ||
return lastChild ? resolve(resolvedPrevious) : resolvedPrevious; | ||
} | ||
function combineFieldValues(data) { | ||
const output = Object.entries(data).reduce((previous, [key, value]) => { | ||
const arrayIndex = key.match(/\[\d+\]$/gi); | ||
if (arrayIndex) { | ||
const index = arrayIndex[0].slice(1, -1); | ||
const filedName = key.substr(0, key.indexOf('[')); | ||
if (!previous[filedName]) | ||
previous[filedName] = []; | ||
previous[filedName][index] = value; | ||
} | ||
else { | ||
previous[key] = value; | ||
} | ||
return previous; | ||
}, {}); | ||
return Object.entries(output).reduce((previous, [key, value]) => { | ||
if (Array.isArray(value)) { | ||
previous[key] = value.filter(Boolean); | ||
return previous; | ||
} | ||
previous[key] = value; | ||
return previous; | ||
}, {}); | ||
} | ||
function useForm({ mode, validationSchema } = { | ||
mode: 'onSubmit', | ||
}) { | ||
const fields = useRef({}); | ||
const localErrorMessages = useRef({}); | ||
const watchFields = useRef({}); | ||
const [errors, updateErrorMessage] = useState({}); | ||
const fieldsRef = useRef({}); | ||
const errorMessagesRef = useRef({}); | ||
const watchFieldsRef = useRef({}); | ||
const [errors, setErrors] = useState({}); | ||
async function validateWithStateUpdate({ target: { name }, type }) { | ||
const ref = fields.current[name]; | ||
const error = await validateField(ref, fields.current); | ||
if (localErrorMessages.current[name] !== error[name] || | ||
const ref = fieldsRef.current[name]; | ||
const error = await validateField(ref, fieldsRef.current); | ||
const errorMessage = errorMessagesRef.current; | ||
if (errorMessage[name] !== error[name] || | ||
mode === 'onChange' || | ||
(mode === 'onBlur' && type === 'blur') || | ||
watchFields.current[name]) { | ||
const copy = Object.assign({}, localErrorMessages.current, error); | ||
watchFieldsRef.current[name]) { | ||
const copy = Object.assign({}, errorMessage, error); | ||
if (!error[name]) | ||
delete copy[name]; | ||
localErrorMessages.current = copy; | ||
updateErrorMessage(copy); | ||
errorMessagesRef.current = copy; | ||
setErrors(copy); | ||
} | ||
@@ -331,3 +391,3 @@ } | ||
target: data, | ||
fields: fields.current, | ||
fields: fieldsRef.current, | ||
validateWithStateUpdate, | ||
@@ -342,14 +402,14 @@ forceDelete, | ||
} | ||
let radioOptionIndex; | ||
const inputData = Object.assign({}, data, { ref: elementRef }); | ||
let radioOptionIndex; | ||
const { ref, required, validate, ref: { name, type, value }, } = inputData; | ||
const allFields = fields.current; | ||
const fields = fieldsRef.current; | ||
if (isRadioInput(type)) { | ||
if (!allFields[name]) { | ||
allFields[name] = { options: [], required, validate, ref: { type: 'radio', name } }; | ||
if (!fields[name]) { | ||
fields[name] = { options: [], required, validate, ref: { type: 'radio', name } }; | ||
} | ||
if (!allFields[name].validate && validate) { | ||
allFields[name].validate = validate; | ||
if (!fields[name].validate && validate) { | ||
fields[name].validate = validate; | ||
} | ||
const options = allFields[name].options || []; | ||
const options = fields[name].options || []; | ||
radioOptionIndex = options.findIndex(({ ref }) => value === ref.value); | ||
@@ -365,11 +425,11 @@ if (radioOptionIndex > -1) { | ||
else { | ||
const isInitialCreate = !allFields[name]; | ||
allFields[name] = Object.assign({}, allFields[name], inputData); | ||
const isInitialCreate = !fields[name]; | ||
fields[name] = Object.assign({}, fields[name], inputData); | ||
if (isInitialCreate) { | ||
allFields[name].mutationWatcher = onDomRemove(ref, () => removeReferenceAndEventListeners(inputData, true)); | ||
fields[name].mutationWatcher = onDomRemove(ref, () => removeReferenceAndEventListeners(inputData, true)); | ||
} | ||
} | ||
attachEventListeners({ | ||
allFields, | ||
watchFields, | ||
fields, | ||
watchFields: watchFieldsRef, | ||
ref, | ||
@@ -396,23 +456,22 @@ type, | ||
function watch(filedNames, defaultValue) { | ||
const watchFields = watchFieldsRef.current; | ||
if (typeof filedNames === 'string') { | ||
if (!watchFields.current[filedNames]) | ||
watchFields.current[filedNames] = true; | ||
if (!watchFields[filedNames]) | ||
watchFields[filedNames] = true; | ||
} | ||
else if (Array.isArray(filedNames)) { | ||
filedNames.forEach(name => { | ||
if (!watchFields.current[name]) | ||
if (!watchFields[name]) | ||
return; | ||
watchFields.current[name] = true; | ||
watchFields[name] = true; | ||
}); | ||
} | ||
else { | ||
Object.values(fields.current).forEach(({ ref }) => { | ||
if (!ref) | ||
Object.values(fieldsRef.current).forEach(({ ref }) => { | ||
if (!ref || !watchFields[ref.name]) | ||
return; | ||
if (!watchFields.current[ref.name]) | ||
return; | ||
watchFields.current[ref.name] = true; | ||
watchFields[ref.name] = true; | ||
}); | ||
} | ||
const result = getFieldsValues(fields.current, filedNames); | ||
const result = getFieldsValues(fieldsRef.current, filedNames); | ||
return result === undefined ? defaultValue : result; | ||
@@ -423,14 +482,15 @@ } | ||
e.persist(); | ||
const allFields = fields.current; | ||
let localErrors; | ||
let values; | ||
let result; | ||
let fieldErrors; | ||
let fieldValues; | ||
const fields = fieldsRef.current; | ||
const currentFieldValues = Object.values(fields); | ||
const fieldsLength = currentFieldValues.length; | ||
if (validationSchema) { | ||
values = Object.values(allFields).reduce((previous, { ref }) => { | ||
previous[ref.name] = getFieldValue(allFields, ref); | ||
fieldValues = currentFieldValues.reduce((previous, { ref }) => { | ||
previous[ref.name] = getFieldValue(fields, ref); | ||
return previous; | ||
}, {}); | ||
localErrors = await validateWithSchema(validationSchema, values); | ||
if (localErrors === undefined) { | ||
callback(values, e); | ||
fieldErrors = await validateWithSchema(validationSchema, fieldValues); | ||
if (fieldErrors === undefined) { | ||
callback(combineFieldValues(fieldValues), e); | ||
return; | ||
@@ -440,54 +500,36 @@ } | ||
else { | ||
const allFieldsValues = Object.values(allFields); | ||
result = await new Promise(resolve => allFieldsValues.reduce(async (previous, data, index) => { | ||
const resolvedPrevious = await previous; | ||
const { ref, ref: { name, type }, options, } = data; | ||
const lastChild = allFieldsValues.length - 1 === index; | ||
removeReferenceAndEventListeners(data); | ||
if (!fields.current[name]) | ||
return lastChild ? resolve(resolvedPrevious) : resolvedPrevious; | ||
const fieldError = await validateField(data, allFields); | ||
const hasError = fieldError && fieldError[name]; | ||
if (!hasError) { | ||
resolvedPrevious.values[name] = getFieldValue(allFields, ref); | ||
return lastChild ? resolve(resolvedPrevious) : resolvedPrevious; | ||
} | ||
if (isRadioInput(type) && Array.isArray(options)) { | ||
options.forEach(option => { | ||
if (option.eventAttached && option.eventAttached.includes('change')) | ||
return; | ||
option.ref.addEventListener('change', validateWithStateUpdate); | ||
option.eventAttached = [...(option.eventAttached || []), 'change']; | ||
}); | ||
// @ts-ignore | ||
} | ||
else if (!fields.current[name].eventAttached || !fields.current[name].eventAttached.includes('input')) { | ||
ref.addEventListener('input', validateWithStateUpdate); | ||
data.eventAttached = [...(data.eventAttached || []), 'input']; | ||
} | ||
resolvedPrevious.localErrors = Object.assign({}, (resolvedPrevious.localErrors || []), fieldError); | ||
return lastChild ? resolve(resolvedPrevious) : resolvedPrevious; | ||
const result = await new Promise(resolve => currentFieldValues.reduce(async (previous, [name, data], index) => { | ||
return await validateAllFields({ | ||
previous, | ||
data, | ||
index, | ||
fieldsLength, | ||
allFields: fields, | ||
removeReferenceAndEventListeners, | ||
validateWithStateUpdate, | ||
resolve, | ||
}); | ||
}, Promise.resolve({ | ||
localErrors: {}, | ||
errors: {}, | ||
values: {}, | ||
}))); | ||
localErrors = result.localErrors; | ||
values = result.values; | ||
fieldErrors = result.errors; | ||
fieldValues = result.values; | ||
} | ||
if (JSON.stringify(omitRefs(localErrorMessages.current)) !== JSON.stringify(omitRefs(localErrors))) { | ||
updateErrorMessage(localErrors); | ||
localErrorMessages.current = localErrors; | ||
if (JSON.stringify(omitRefs(errorMessagesRef.current)) !== JSON.stringify(omitRefs(fieldErrors))) { | ||
setErrors(fieldErrors); | ||
errorMessagesRef.current = fieldErrors; | ||
} | ||
if (!Object.values(localErrors).length) | ||
callback(values, e); | ||
if (!Object.values(fieldErrors).length) | ||
callback(combineFieldValues(fieldValues), e); | ||
}; | ||
useEffect(() => () => { | ||
fields.current && | ||
Object.values(fields.current).forEach(({ ref, options }) => isRadioInput(ref.type) && Array.isArray(options) | ||
fieldsRef.current && | ||
Object.values(fieldsRef.current).forEach(({ ref, options }) => isRadioInput(ref.type) && Array.isArray(options) | ||
? options.forEach(({ ref }) => removeAllEventListeners(ref, validateWithStateUpdate)) | ||
: removeAllEventListeners(ref, validateWithStateUpdate)); | ||
fields.current = {}; | ||
watchFields.current = {}; | ||
localErrorMessages.current = {}; | ||
updateErrorMessage({}); | ||
fieldsRef.current = {}; | ||
watchFieldsRef.current = {}; | ||
errorMessagesRef.current = {}; | ||
setErrors({}); | ||
}, [mode]); | ||
@@ -494,0 +536,0 @@ return { |
@@ -42,3 +42,6 @@ 'use strict'; | ||
function getFieldsValues(fields, filedNames) { | ||
return Object.values(fields).reduce((previous, { ref, ref: { name } }) => { | ||
return Object.values(fields).reduce((previous, data) => { | ||
if (Array.isArray(data)) | ||
return; | ||
const { ref, ref: { name } } = data; | ||
const value = getFieldValue(fields, ref); | ||
@@ -84,2 +87,3 @@ if (typeof filedNames === 'string') { | ||
message: required, | ||
// @ts-ignore | ||
ref: isRadio ? fields[name].options[0].ref : ref, | ||
@@ -141,3 +145,3 @@ }; | ||
if (typeof result !== 'boolean' || !result) { | ||
copy[name] = Object.assign({}, copy[name], { type: 'validate', message: result || true, ref: isRadio ? options[0].ref : ref }); | ||
copy[name] = Object.assign({}, copy[name], { type: 'validate', message: result || true, ref: isRadio && options ? options[0].ref : ref }); | ||
return copy; | ||
@@ -166,3 +170,3 @@ } | ||
if (Object.keys(result).length) { | ||
copy[name] = Object.assign({}, copy[name], { ref: isRadio ? options[0].ref : ref }, result); | ||
copy[name] = Object.assign({}, copy[name], { ref: isRadio && options ? options[0].ref : ref }, result); | ||
return copy; | ||
@@ -228,5 +232,5 @@ } | ||
function attachEventListeners({ mode, allFields, watchFields, radioOptionIndex, ref, type, name, validateWithStateUpdate, }) { | ||
const field = allFields[name]; | ||
const isOnChange = mode === 'onChange' || watchFields.current[ref.name]; | ||
function attachEventListeners({ mode, fields, watchFields, radioOptionIndex, ref, type, name, validateWithStateUpdate, }) { | ||
const field = fields[name]; | ||
const isOnChange = mode === 'onChange' || watchFields[ref.name]; | ||
const isOnBlur = mode === 'onBlur'; | ||
@@ -308,21 +312,77 @@ if (!field || (!isOnChange && !isOnBlur)) | ||
async function validateAllFields({ previous, data, index, fieldsLength, resolve, allFields, removeReferenceAndEventListeners, validateWithStateUpdate, }) { | ||
const resolvedPrevious = await previous; | ||
const { ref, ref: { name, type }, options, } = data; | ||
const lastChild = fieldsLength - 1 === index; | ||
removeReferenceAndEventListeners(data); | ||
if (!allFields[name]) | ||
return lastChild ? resolve(resolvedPrevious) : resolvedPrevious; | ||
const fieldError = await validateField(data, allFields); | ||
const hasError = fieldError && fieldError[name]; | ||
if (!hasError) { | ||
resolvedPrevious.values[name] = getFieldValue(allFields, ref); | ||
return lastChild ? resolve(resolvedPrevious) : resolvedPrevious; | ||
} | ||
if (isRadioInput(type) && Array.isArray(options)) { | ||
options.forEach(option => { | ||
if (option.eventAttached && option.eventAttached.includes('change')) | ||
return; | ||
option.ref.addEventListener('change', validateWithStateUpdate); | ||
option.eventAttached = [...(option.eventAttached || []), 'change']; | ||
}); | ||
// @ts-ignore | ||
} | ||
else if (!data.eventAttached || !data.eventAttached.includes('input')) { | ||
ref.addEventListener('input', validateWithStateUpdate); | ||
data.eventAttached = [...(data.eventAttached || []), 'input']; | ||
} | ||
resolvedPrevious.errors = Object.assign({}, (resolvedPrevious.errors || []), fieldError); | ||
return lastChild ? resolve(resolvedPrevious) : resolvedPrevious; | ||
} | ||
function combineFieldValues(data) { | ||
const output = Object.entries(data).reduce((previous, [key, value]) => { | ||
const arrayIndex = key.match(/\[\d+\]$/gi); | ||
if (arrayIndex) { | ||
const index = arrayIndex[0].slice(1, -1); | ||
const filedName = key.substr(0, key.indexOf('[')); | ||
if (!previous[filedName]) | ||
previous[filedName] = []; | ||
previous[filedName][index] = value; | ||
} | ||
else { | ||
previous[key] = value; | ||
} | ||
return previous; | ||
}, {}); | ||
return Object.entries(output).reduce((previous, [key, value]) => { | ||
if (Array.isArray(value)) { | ||
previous[key] = value.filter(Boolean); | ||
return previous; | ||
} | ||
previous[key] = value; | ||
return previous; | ||
}, {}); | ||
} | ||
function useForm({ mode, validationSchema } = { | ||
mode: 'onSubmit', | ||
}) { | ||
const fields = react.useRef({}); | ||
const localErrorMessages = react.useRef({}); | ||
const watchFields = react.useRef({}); | ||
const [errors, updateErrorMessage] = react.useState({}); | ||
const fieldsRef = react.useRef({}); | ||
const errorMessagesRef = react.useRef({}); | ||
const watchFieldsRef = react.useRef({}); | ||
const [errors, setErrors] = react.useState({}); | ||
async function validateWithStateUpdate({ target: { name }, type }) { | ||
const ref = fields.current[name]; | ||
const error = await validateField(ref, fields.current); | ||
if (localErrorMessages.current[name] !== error[name] || | ||
const ref = fieldsRef.current[name]; | ||
const error = await validateField(ref, fieldsRef.current); | ||
const errorMessage = errorMessagesRef.current; | ||
if (errorMessage[name] !== error[name] || | ||
mode === 'onChange' || | ||
(mode === 'onBlur' && type === 'blur') || | ||
watchFields.current[name]) { | ||
const copy = Object.assign({}, localErrorMessages.current, error); | ||
watchFieldsRef.current[name]) { | ||
const copy = Object.assign({}, errorMessage, error); | ||
if (!error[name]) | ||
delete copy[name]; | ||
localErrorMessages.current = copy; | ||
updateErrorMessage(copy); | ||
errorMessagesRef.current = copy; | ||
setErrors(copy); | ||
} | ||
@@ -333,3 +393,3 @@ } | ||
target: data, | ||
fields: fields.current, | ||
fields: fieldsRef.current, | ||
validateWithStateUpdate, | ||
@@ -344,14 +404,14 @@ forceDelete, | ||
} | ||
let radioOptionIndex; | ||
const inputData = Object.assign({}, data, { ref: elementRef }); | ||
let radioOptionIndex; | ||
const { ref, required, validate, ref: { name, type, value }, } = inputData; | ||
const allFields = fields.current; | ||
const fields = fieldsRef.current; | ||
if (isRadioInput(type)) { | ||
if (!allFields[name]) { | ||
allFields[name] = { options: [], required, validate, ref: { type: 'radio', name } }; | ||
if (!fields[name]) { | ||
fields[name] = { options: [], required, validate, ref: { type: 'radio', name } }; | ||
} | ||
if (!allFields[name].validate && validate) { | ||
allFields[name].validate = validate; | ||
if (!fields[name].validate && validate) { | ||
fields[name].validate = validate; | ||
} | ||
const options = allFields[name].options || []; | ||
const options = fields[name].options || []; | ||
radioOptionIndex = options.findIndex(({ ref }) => value === ref.value); | ||
@@ -367,11 +427,11 @@ if (radioOptionIndex > -1) { | ||
else { | ||
const isInitialCreate = !allFields[name]; | ||
allFields[name] = Object.assign({}, allFields[name], inputData); | ||
const isInitialCreate = !fields[name]; | ||
fields[name] = Object.assign({}, fields[name], inputData); | ||
if (isInitialCreate) { | ||
allFields[name].mutationWatcher = onDomRemove(ref, () => removeReferenceAndEventListeners(inputData, true)); | ||
fields[name].mutationWatcher = onDomRemove(ref, () => removeReferenceAndEventListeners(inputData, true)); | ||
} | ||
} | ||
attachEventListeners({ | ||
allFields, | ||
watchFields, | ||
fields, | ||
watchFields: watchFieldsRef, | ||
ref, | ||
@@ -398,23 +458,22 @@ type, | ||
function watch(filedNames, defaultValue) { | ||
const watchFields = watchFieldsRef.current; | ||
if (typeof filedNames === 'string') { | ||
if (!watchFields.current[filedNames]) | ||
watchFields.current[filedNames] = true; | ||
if (!watchFields[filedNames]) | ||
watchFields[filedNames] = true; | ||
} | ||
else if (Array.isArray(filedNames)) { | ||
filedNames.forEach(name => { | ||
if (!watchFields.current[name]) | ||
if (!watchFields[name]) | ||
return; | ||
watchFields.current[name] = true; | ||
watchFields[name] = true; | ||
}); | ||
} | ||
else { | ||
Object.values(fields.current).forEach(({ ref }) => { | ||
if (!ref) | ||
Object.values(fieldsRef.current).forEach(({ ref }) => { | ||
if (!ref || !watchFields[ref.name]) | ||
return; | ||
if (!watchFields.current[ref.name]) | ||
return; | ||
watchFields.current[ref.name] = true; | ||
watchFields[ref.name] = true; | ||
}); | ||
} | ||
const result = getFieldsValues(fields.current, filedNames); | ||
const result = getFieldsValues(fieldsRef.current, filedNames); | ||
return result === undefined ? defaultValue : result; | ||
@@ -425,14 +484,15 @@ } | ||
e.persist(); | ||
const allFields = fields.current; | ||
let localErrors; | ||
let values; | ||
let result; | ||
let fieldErrors; | ||
let fieldValues; | ||
const fields = fieldsRef.current; | ||
const currentFieldValues = Object.values(fields); | ||
const fieldsLength = currentFieldValues.length; | ||
if (validationSchema) { | ||
values = Object.values(allFields).reduce((previous, { ref }) => { | ||
previous[ref.name] = getFieldValue(allFields, ref); | ||
fieldValues = currentFieldValues.reduce((previous, { ref }) => { | ||
previous[ref.name] = getFieldValue(fields, ref); | ||
return previous; | ||
}, {}); | ||
localErrors = await validateWithSchema(validationSchema, values); | ||
if (localErrors === undefined) { | ||
callback(values, e); | ||
fieldErrors = await validateWithSchema(validationSchema, fieldValues); | ||
if (fieldErrors === undefined) { | ||
callback(combineFieldValues(fieldValues), e); | ||
return; | ||
@@ -442,54 +502,36 @@ } | ||
else { | ||
const allFieldsValues = Object.values(allFields); | ||
result = await new Promise(resolve => allFieldsValues.reduce(async (previous, data, index) => { | ||
const resolvedPrevious = await previous; | ||
const { ref, ref: { name, type }, options, } = data; | ||
const lastChild = allFieldsValues.length - 1 === index; | ||
removeReferenceAndEventListeners(data); | ||
if (!fields.current[name]) | ||
return lastChild ? resolve(resolvedPrevious) : resolvedPrevious; | ||
const fieldError = await validateField(data, allFields); | ||
const hasError = fieldError && fieldError[name]; | ||
if (!hasError) { | ||
resolvedPrevious.values[name] = getFieldValue(allFields, ref); | ||
return lastChild ? resolve(resolvedPrevious) : resolvedPrevious; | ||
} | ||
if (isRadioInput(type) && Array.isArray(options)) { | ||
options.forEach(option => { | ||
if (option.eventAttached && option.eventAttached.includes('change')) | ||
return; | ||
option.ref.addEventListener('change', validateWithStateUpdate); | ||
option.eventAttached = [...(option.eventAttached || []), 'change']; | ||
}); | ||
// @ts-ignore | ||
} | ||
else if (!fields.current[name].eventAttached || !fields.current[name].eventAttached.includes('input')) { | ||
ref.addEventListener('input', validateWithStateUpdate); | ||
data.eventAttached = [...(data.eventAttached || []), 'input']; | ||
} | ||
resolvedPrevious.localErrors = Object.assign({}, (resolvedPrevious.localErrors || []), fieldError); | ||
return lastChild ? resolve(resolvedPrevious) : resolvedPrevious; | ||
const result = await new Promise(resolve => currentFieldValues.reduce(async (previous, [name, data], index) => { | ||
return await validateAllFields({ | ||
previous, | ||
data, | ||
index, | ||
fieldsLength, | ||
allFields: fields, | ||
removeReferenceAndEventListeners, | ||
validateWithStateUpdate, | ||
resolve, | ||
}); | ||
}, Promise.resolve({ | ||
localErrors: {}, | ||
errors: {}, | ||
values: {}, | ||
}))); | ||
localErrors = result.localErrors; | ||
values = result.values; | ||
fieldErrors = result.errors; | ||
fieldValues = result.values; | ||
} | ||
if (JSON.stringify(omitRefs(localErrorMessages.current)) !== JSON.stringify(omitRefs(localErrors))) { | ||
updateErrorMessage(localErrors); | ||
localErrorMessages.current = localErrors; | ||
if (JSON.stringify(omitRefs(errorMessagesRef.current)) !== JSON.stringify(omitRefs(fieldErrors))) { | ||
setErrors(fieldErrors); | ||
errorMessagesRef.current = fieldErrors; | ||
} | ||
if (!Object.values(localErrors).length) | ||
callback(values, e); | ||
if (!Object.values(fieldErrors).length) | ||
callback(combineFieldValues(fieldValues), e); | ||
}; | ||
react.useEffect(() => () => { | ||
fields.current && | ||
Object.values(fields.current).forEach(({ ref, options }) => isRadioInput(ref.type) && Array.isArray(options) | ||
fieldsRef.current && | ||
Object.values(fieldsRef.current).forEach(({ ref, options }) => isRadioInput(ref.type) && Array.isArray(options) | ||
? options.forEach(({ ref }) => removeAllEventListeners(ref, validateWithStateUpdate)) | ||
: removeAllEventListeners(ref, validateWithStateUpdate)); | ||
fields.current = {}; | ||
watchFields.current = {}; | ||
localErrorMessages.current = {}; | ||
updateErrorMessage({}); | ||
fieldsRef.current = {}; | ||
watchFieldsRef.current = {}; | ||
errorMessagesRef.current = {}; | ||
setErrors({}); | ||
}, [mode]); | ||
@@ -496,0 +538,0 @@ return { |
@@ -1,4 +0,4 @@ | ||
export default function attachEventListeners({ mode, allFields, watchFields, radioOptionIndex, ref, type, name, validateWithStateUpdate, }: { | ||
export default function attachEventListeners({ mode, fields, watchFields, radioOptionIndex, ref, type, name, validateWithStateUpdate, }: { | ||
mode: any; | ||
allFields: any; | ||
fields: any; | ||
watchFields: any; | ||
@@ -5,0 +5,0 @@ radioOptionIndex: any; |
@@ -12,2 +12,3 @@ ## Examples | ||
| Async Submit Validation | https://codesandbox.io/s/xrjv48o0qp | | ||
| Array Fields | https://codesandbox.io/s/6j1760jkjk | | ||
| Async Field Validation | https://codesandbox.io/s/m5pj55yj7x | | ||
@@ -14,0 +15,0 @@ | Normalize/Format/Mask Field | https://codesandbox.io/s/387z7njwzp | |
{ | ||
"name": "react-hook-form", | ||
"version": "2.0.3", | ||
"version": "2.1.0-beta.1", | ||
"main": "dist/index.js", | ||
@@ -5,0 +5,0 @@ "module": "dist/index.es.js", |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
69450
47
1737
2