Skip to content

Commit

Permalink
feat: added logic to unregister fields when schema changes
Browse files Browse the repository at this point in the history
  • Loading branch information
keyurparalkar committed Apr 16, 2024
1 parent be058aa commit 669816a
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 9 deletions.
43 changes: 35 additions & 8 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -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);

Expand All @@ -21,39 +22,65 @@ export type TFormValues = typeof FormValues;

function App() {
const [value, setValue] = useState(schema);
const previousSchema = useRef<typeof value | null>(null);
const [editorError, setEditorError] = useState<ZodError>();

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<TFormValues>();
} = useForm<TFormValues>({
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 (
<div className="grid grid-rows-[70px_1fr_40px] m-5 gap-2">
<h1 className="text-4xl font-bold">JSON to Form</h1>
Expand All @@ -69,7 +96,7 @@ function App() {
/>
</div>
<div id="form-container" className="border border-slate-300">
<form className="my-5 mx-10" onSubmit={handleSubmit(onSubmit)}>
<form className="my-8 mx-32" onSubmit={handleSubmit(onSubmit)}>
<SchemaRenderer
schema={value}
errors={errors}
Expand All @@ -81,7 +108,7 @@ function App() {
</div>

<div id="error-status-bar" className="bg-gray-400">
{errorMessage && JSON.stringify(errorMessage.issues[0])}
{JSON.stringify(errorMessage)}
</div>
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion src/interfaces/field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
20 changes: 20 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -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<string, string> = {};

schema.fields.forEach((field) => {
if (field.type === "field") {
temp[field.accessorKey] = "";
} else {
temp = { ...temp, ...getFormValuesFromSchema(field) };
}
});

return temp;
};

0 comments on commit 669816a

Please sign in to comment.