From 669816a6c9350ba8929f75fd8cfaaa8cf67b6c52 Mon Sep 17 00:00:00 2001 From: keyurparalkar Date: Tue, 16 Apr 2024 16:13:59 +0530 Subject: [PATCH] feat: added logic to unregister fields when schema changes --- src/App.tsx | 43 +++++++++++++++++++++++++++++++++-------- src/interfaces/field.ts | 2 +- src/utils.ts | 20 +++++++++++++++++++ 3 files changed, 56 insertions(+), 9 deletions(-) create mode 100644 src/utils.ts diff --git a/src/App.tsx b/src/App.tsx index ca05ade..cf55eaa 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,12 +1,13 @@ -import { useCallback, useMemo, useState } from "react"; +import { useEffect, useMemo, useRef, useState } from "react"; import { useForm } from "react-hook-form"; import CodeMirror from "@uiw/react-codemirror"; import { json } from "@codemirror/lang-json"; +import { ZodError } from "zod"; import personalInfoSchema from "./schemas/forms/personal_info.json"; import { SchemaDef } from "./interfaces/schema"; import SchemaRenderer from "./components/ui/SchemaRenderer"; -import { ZodError } from "zod"; +import { getFormValuesFromSchema, symmetricDiff } from "./utils"; const schema = SchemaDef.parse(personalInfoSchema); @@ -21,39 +22,65 @@ export type TFormValues = typeof FormValues; function App() { const [value, setValue] = useState(schema); + const previousSchema = useRef(null); const [editorError, setEditorError] = useState(); - const onChange = useCallback((val: string) => { + const onChange = (val: string) => { try { + // Save the previous schema value + previousSchema.current = value; const tempSchema = SchemaDef.parse(JSON.parse(val)); setValue(tempSchema); setEditorError(undefined); } catch (error) { + console.log({ error }); setEditorError(error as ZodError); } - }, []); + }; const errorMessage = useMemo(() => { - if (editorError && editorError.issues[0]) { + if (editorError && editorError.issues?.[0]) { const firstIssue = editorError.issues[0]; return firstIssue.code === "invalid_union" ? firstIssue.unionErrors[0] : ""; } + + return editorError; }, [editorError]); const { register, + unregister, handleSubmit, reset, formState: { errors }, - } = useForm(); + } = useForm({ + shouldUseNativeValidation: true, + }); const onSubmit = (data: TFormValues) => { console.log("form Data = ", data); reset(); }; + // Effect that finds the diff and unregister's the field on schema change. + useEffect(() => { + const previousValues = previousSchema.current + ? getFormValuesFromSchema(previousSchema.current) + : {}; + const currentValues = getFormValuesFromSchema(value); + + const diff = symmetricDiff( + Object.keys(currentValues), + Object.keys(previousValues) + ); + + diff.forEach((fieldName) => { + unregister(fieldName); + }); + }, [unregister, value]); + return (

JSON to Form

@@ -69,7 +96,7 @@ function App() { />
-
+
- {errorMessage && JSON.stringify(errorMessage.issues[0])} + {JSON.stringify(errorMessage)}
); diff --git a/src/interfaces/field.ts b/src/interfaces/field.ts index d8a9fbd..1358c67 100644 --- a/src/interfaces/field.ts +++ b/src/interfaces/field.ts @@ -24,7 +24,7 @@ export const FieldTypeDef = z.object({ type: z.literal("field"), dataType: z.enum(["text", "number", "email"]), fieldName: z.string(), - accessorKey: z.string(), + accessorKey: z.string().min(1, { message: "accessorKey cannot be empty" }), validation: z .object({ required: RequiredDef.optional(), diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..083c3ec --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,20 @@ +import { SchemaType } from "./interfaces/schema"; + +export const symmetricDiff = (arr1: string[], arr2: string[]) => + arr1 + .filter((x) => !arr2.includes(x)) + .concat(arr2.filter((x) => !arr1.includes(x))); + +export const getFormValuesFromSchema = (schema: SchemaType) => { + let temp: Record = {}; + + schema.fields.forEach((field) => { + if (field.type === "field") { + temp[field.accessorKey] = ""; + } else { + temp = { ...temp, ...getFormValuesFromSchema(field) }; + } + }); + + return temp; +};