diff --git a/.changeset/tasty-colts-watch.md b/.changeset/tasty-colts-watch.md new file mode 100644 index 00000000..e8c25526 --- /dev/null +++ b/.changeset/tasty-colts-watch.md @@ -0,0 +1,5 @@ +--- +"react-cool-form": patch +--- + +feat: use unset for clear error diff --git a/examples/src/BasicForm/index.tsx b/examples/src/BasicForm/index.tsx index 656cfc98..06b8214d 100644 --- a/examples/src/BasicForm/index.tsx +++ b/examples/src/BasicForm/index.tsx @@ -128,16 +128,16 @@ export default (): JSX.Element => { console.log( "LOG ===> formState: ", getState({ - values: "values", - touched: "touched", + // values: "values", + // touched: "touched", errors: "errors", - isDirty: "isDirty", - dirtyFields: "dirtyFields", - isValidating: "isValidating", - isValid: "isValid", - isSubmitting: "isSubmitting", - isSubmitted: "isSubmitted", - submitCount: "submitCount", + // isDirty: "isDirty", + // dirtyFields: "dirtyFields", + // isValidating: "isValidating", + // isValid: "isValid", + // isSubmitting: "isSubmitting", + // isSubmitted: "isSubmitted", + // submitCount: "submitCount", }) ); @@ -181,7 +181,8 @@ export default (): JSX.Element => { }; const handleClearErrorsClick = (): void => { - setFieldError("text.nest"); + // setFieldError("text.nest"); + setFieldError("number"); }; const handleValidateClick = (): void => { @@ -232,9 +233,9 @@ export default (): JSX.Element => { { + ref={validate(async (value) => { return value.length <= 5 ? "Field error" : ""; - })} */ + })} /> )} @@ -243,9 +244,9 @@ export default (): JSX.Element => { { + ref={validate(async (value) => { return value.length <= 5 ? "Field error" : ""; - })} */ + })} /> )} @@ -254,9 +255,9 @@ export default (): JSX.Element => { label="Number:" type="number" name="number" - /* ref={validate((value) => { + ref={validate((value) => { return value <= 5 ? "Field error" : ""; - })} */ + })} /> diff --git a/src/useForm.ts b/src/useForm.ts index 64c4aae6..1f2cdf77 100644 --- a/src/useForm.ts +++ b/src/useForm.ts @@ -46,6 +46,7 @@ import { isRangeField, isUndefined, set, + unset, warn, } from "./utils"; @@ -215,14 +216,16 @@ export default ({ ? error(get(stateRef.current.errors, name)) : error; - if (isKey(name) && !error) { + if (error) { + setStateRef(`errors.${name}`, error); + } else { setErrors((prevErrors) => { + if (!isKey(name)) return unset(prevErrors, name, true); + const nextErrors = { ...prevErrors }; delete nextErrors[name]; return nextErrors; }); - } else { - setStateRef(`errors.${name}`, error || undefined); } }, [setErrors, setStateRef, stateRef] diff --git a/src/utils.ts b/src/utils.ts index 7f94fb78..015a1e98 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -82,23 +82,50 @@ const cloneObject = (object: unknown): any => { }; export const set: Set = (object, path, value, immutable = false) => { - if (!isPlainObject(object)) return object; + if (!isPlainObject(object)) throw new TypeError("Expected an object."); - const tempPath = String(path) + const segs = String(path) .split(/[.[\]]+/) .filter(Boolean); const newObject = immutable ? cloneObject(object) : object; - tempPath.slice(0, -1).reduce((obj, key, idx) => { + segs.slice(0, -1).reduce((obj, key, idx) => { if (isObject(obj[key])) return obj[key]; - const next = Number(tempPath[idx + 1]); + const next = Number(segs[idx + 1]); obj[key] = Number.isInteger(next) && next >= 0 ? [] : {}; return obj[key]; - }, newObject)[tempPath[tempPath.length - 1] || ""] = value; + }, newObject)[segs[segs.length - 1] || ""] = value; return newObject; }; +export const unset = (object: any, path: string, immutable = false): any => { + if (!isPlainObject(object)) throw new TypeError("Expected an object."); + + const refObject = immutable ? cloneObject(object) : object; + let newObject = refObject; + + // eslint-disable-next-line no-prototype-builtins + if (newObject.hasOwnProperty(path)) { + delete newObject[path]; + return refObject; + } + + if (!isUndefined(get(newObject, path))) { + const segs = path.split("."); + let last = segs.pop() as string; + + while (segs.length && segs[segs.length - 1].slice(-1) === "\\") + last = `${(segs.pop() as string).slice(0, -1)}.${last}`; + + while (segs.length) newObject = newObject[(path = segs.shift() as string)]; + + delete newObject[last]; + } + + return refObject; +}; + export const deepMerge = (...objects: any[]) => objects.reduce((prev, obj) => { Object.keys(obj).forEach((key) => {