Reputation: 1171
I have a base interface for a form, but each of my pages extends that to create an extended form. Now I want to create a child component that generates a form based on the extended template.
// childComponent.tsx
export default function ProductSourceFormTemplate<T extends IProductSourceBase>({ formSchema }: {
formSchema: z.ZodType<T>,
}) {
type FormValues = z.infer<typeof formSchema>
const defaultValues: T = useMemo<T>(() => ({
sourceId: ""
}) as T, [searchParams])
const form = useForm<FormValues>({
resolver: zodResolver(formSchema),
defaultValues: defaultValues,
});
// render form
}
but it gives this error on defaultValues
Type 'T' is not assignable to type 'AsyncDefaultValues<T> | DefaultValues<T> | undefined'.
Type 'IProductSourceBase' is not assignable to type 'AsyncDefaultValues<T> | DefaultValues<T> | undefined'.
Type 'IProductSourceBase' is not assignable to type 'DefaultValues<T>'.ts(2322)
and also none of the names are recognised when I want to create form elements.
What I want is for my parent component to create productBaseSchema.extend({})
which is a z.ZodObject<{}>
and then pass this extended schema to the child const extendedSchema: z.ZodObject<z.objectUtil.extendShape<{}>
that can also give onSubmit, render functions in its props so only parent only deals with extra fields.
Examples:
export interface IProductSourceBase {
productId: string;
sourceId: number;
sourceMeta?: any;
}
const productSourceBaseSchema = z.object({
productId: z.string().nonempty(translate("Product ID is required")),
sourceId: z.coerce.number().int(translate("Source ID must be an integer")).gt(0, translate("A source must be selected")),
});
const extendedSchema = productSourceBaseSchema.extend({
sourceMeta: z.object({
sourceUrl: z.string().url(translate("This is not a valid URL")).nonempty(translate("Url is required")).refine(
(url) => /\/spreadsheets\/d\/([a-zA-Z0-9-_]{16,44})/.test(url), // Test for valid Spreadsheet ID
{
message: translate("This is not a valid Google Spreadsheet ID"), // Custom error message
}
),
cell: z.string().nonempty(translate("Cell number is required")).refine(
(cell) => /^[A-Za-z]+[1-9]\d*$/.test(cell), // Check if the cell matches the pattern
{
message: translate("Invalid cell reference format"), // Custom error message for invalid format
}
),
}),
});
Upvotes: 0
Views: 48
Reputation: 529
Here, I'd ensure you're using FormValues
as the assignment for both defaultValues
and useForm
, while also using react-hook-form
's generic for DefaultValues
-- this applies a deep partial to the given type (just to clean things up). And, for forms like this, if you're ever incorporating a transform
or default
into your zod
schema, using z.input
rather than z.infer
can make things a bit easier, but doesn't directly apply here.
import { DefaultValues, ... } from "react-hook-form"
...
type FormValues = z.infer<typeof formSchema>
const defaultValues = useMemo<DefaultValues<FormValues>>(() => ({
sourceId: ""
}), [])
const form = useForm<FormValues>({
resolver: zodResolver(formSchema),
defaultValues,
});
...
Upvotes: 0