Skip to content

Commit

Permalink
Merge pull request #82 from iway1/refactor-submitter
Browse files Browse the repository at this point in the history
Refactor submitter
  • Loading branch information
iway1 authored Mar 12, 2023
2 parents ba3381f + d5e94f7 commit ce0a18e
Showing 1 changed file with 70 additions and 42 deletions.
112 changes: 70 additions & 42 deletions src/createSchemaForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,11 @@ function propsMapToObect(propsMap: PropsMapping) {
return r;
}

type RTFFormSchemaType = z.AnyZodObject | ZodEffects<any, any>;
type RTFFormSubmitFn<SchemaType extends RTFFormSchemaType> = (
values: z.infer<SchemaType>
) => void | Promise<void>;

/**
* Creates a reusable, typesafe form component based on a zod-component mapping.
* @example
Expand Down Expand Up @@ -227,9 +232,7 @@ export function createTsForm<
const propsMap = propsMapToObect(
options?.propsMap ? options.propsMap : defaultPropsMap
);
return function Component<
SchemaType extends z.AnyZodObject | ZodEffects<any, any>
>({
return function Component<SchemaType extends RTFFormSchemaType>({
schema,
onSubmit,
props,
Expand All @@ -247,7 +250,7 @@ export function createTsForm<
/**
* A callback function that will be called with the data once the form has been submitted and validated successfully.
*/
onSubmit: (values: z.infer<SchemaType>) => void | Promise<void>;
onSubmit: RTFFormSubmitFn<RTFFormSchemaType>;
/**
* Initializes your form with default values. Is a deep partial, so all properties and nested properties are optional.
*/
Expand Down Expand Up @@ -363,42 +366,12 @@ export function createTsForm<
const { control, handleSubmit, setError } = _form;
const _schema = unwrapEffects(schema);
const shape: Record<string, RTFSupportedZodTypes> = _schema._def.shape();
const coerceUndefinedFieldsRef = useRef<Set<string>>(new Set());

function addToCoerceUndefined(fieldName: string) {
coerceUndefinedFieldsRef.current.add(fieldName);
}

function removeFromCoerceUndefined(fieldName: string) {
coerceUndefinedFieldsRef.current.delete(fieldName);
}

function removeUndefined(data: any) {
const r = { ...data };
for (const undefinedField of coerceUndefinedFieldsRef.current) {
delete r[undefinedField];
}
return r;
}

function _submit(data: z.infer<SchemaType>) {
return resolver(removeUndefined(data), {} as any, {} as any).then(
async (e) => {
const errorKeys = Object.keys(e.errors);
if (!errorKeys.length) {
await onSubmit(data);
return;
}
for (const key of errorKeys) {
setError(
key as any,
(e.errors as any)[key] as unknown as ErrorOption
);
}
}
);
}
const submitFn = handleSubmit(_submit);
const submitter = useSubmitter({
resolver,
onSubmit,
setError,
});
const submitFn = handleSubmit(submitter.submit);
type SchemaKey = keyof z.infer<UnwrapEffects<SchemaType>>;
const renderedFields = Object.keys(shape).reduce(
(accum, key: SchemaKey) => {
Expand Down Expand Up @@ -442,8 +415,8 @@ export function createTsForm<
label={ctxLabel}
placeholder={ctxPlaceholder}
enumValues={meta.enumValues as string[] | undefined}
addToCoerceUndefined={addToCoerceUndefined}
removeFromCoerceUndefined={removeFromCoerceUndefined}
addToCoerceUndefined={submitter.addToCoerceUndefined}
removeFromCoerceUndefined={submitter.removeFromCoerceUndefined}
>
<Component key={key} {...mergedProps} />
</FieldContextProvider>
Expand All @@ -470,3 +443,58 @@ export function createTsForm<
);
};
}
// handles internal custom submit logic
// Implements a workaround to allow devs to set form values to undefined (as it breaks react hook form)
// For example https://github.com/react-hook-form/react-hook-form/discussions/2797
function useSubmitter<SchemaType extends RTFFormSchemaType>({
resolver,
onSubmit,
setError,
}: {
resolver: ReturnType<typeof zodResolver>;
onSubmit: RTFFormSubmitFn<RTFFormSchemaType>;
setError: ReturnType<typeof useForm>["setError"];
}) {
const coerceUndefinedFieldsRef = useRef<Set<string>>(new Set());

function addToCoerceUndefined(fieldName: string) {
coerceUndefinedFieldsRef.current.add(fieldName);
}

function removeFromCoerceUndefined(fieldName: string) {
coerceUndefinedFieldsRef.current.delete(fieldName);
}

function removeUndefined(data: any) {
const r = { ...data };
for (const undefinedField of coerceUndefinedFieldsRef.current) {
delete r[undefinedField];
}
return r;
}

function submit(data: z.infer<SchemaType>) {
return resolver(removeUndefined(data), {} as any, {} as any).then(
async (e) => {
const errorKeys = Object.keys(e.errors);
if (!errorKeys.length) {
await onSubmit(data);
return;
}
for (const key of errorKeys) {
setError(
key as any,
(e.errors as any)[key] as unknown as ErrorOption
);
}
}
);
}

return {
submit,
removeUndefined,
removeFromCoerceUndefined,
addToCoerceUndefined,
};
}

1 comment on commit ce0a18e

@vercel
Copy link

@vercel vercel bot commented on ce0a18e Mar 12, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.