Replies: 1 comment 2 replies
-
Hi @aydrian. Union is tricky to support. But if the discriminator (i.e. Please check this code snippet and see if it works for you: import { useForm } from '@conform-to/react';
import { getFieldsetConstraint, parse } from '@conform-to/zod';
import type { ActionArgs } from '@remix-run/node';
import { json } from '@remix-run/node';
import { Form, useActionData } from '@remix-run/react';
import { z } from 'zod';
const DonationWithLeads = z.object({
eventId: z.string(),
charityId: z.string(),
collectLeads: z.preprocess(value => value === 'on', z.literal(false)),
firstName: z.string().min(1, { message: "First name is required" }),
lastName: z.string().min(1, { message: "Last name is required" }),
email: z.string().email({ message: "Company email is invalid" }),
company: z.string().min(1, { message: "Company is required" }),
jobRole: z.string().min(1, { message: "Job title is required" })
});
const DonationWithoutLeads = z.object({
eventId: z.string(),
charityId: z.string(),
collectLeads: z.preprocess(value => value === 'on', z.literal(false))
});
const DonationFormSchema = z.discriminatedUnion("collectLeads", [
DonationWithLeads,
DonationWithoutLeads,
]);
export async function action({ request }: ActionArgs) {
const formData = await request.formData();
const submission = parse(formData, {
schema: DonationFormSchema,
});
if (!submission.value || submission.intent !== 'submit') {
return json(submission);
}
throw new Error('Not implemented');
}
export default function DonationForm({ collectLeads }) {
// Last submission returned by the server
const lastSubmission = useActionData<typeof action>();
const [form, fieldset] = useForm({
lastSubmission,
// The getFieldsetConstraint helper does not support union or discriminated unions yet
// Cast the result to `any` until type inference is fixed
constraint: getFieldsetConstraint(collectLeads ? DonationWithLeads : DonationWithoutLeads) as any,
onValidate({ formData }) {
return parse(formData, { schema: DonationFormSchema });
},
});
return (
<Form method="post" {...form.props}>
{/* The default value is "on" if checked */}
<input type="checkbox" name="collectLeads" defaultChecked={collectLeads} hidden />
{/* ... */}
</Form>
);
} There are a few issues I noticed that Conform should be improved / fixed:
getFieldsetConstraint(
z.object({ a: z.string() }).or(z.object({ b: z.string() }))
)
// Should both a and b be optional now?
// Like this? { a: { required: false }, b: { required: false } }
getFieldsetConstraint(
z.object({ c: z.string().min(1) }).or(z.object({ c: z.number().min(1) }))
)
// Should c have a minLength attribute or min attribute?
// How about the common constraint? e.g. required
// Like this? { c: { required: true } }
getFieldsetConstraint(
z.discriminatedUnion('d', [
z.object({ d: z.literal('foo'), e: z.string().min(1) }),
z.object({ d: z.literal('bar'), f: z.string().min(1) }),
])
)
// discriminatedUnion does makes it better
// But now we need the value of the discrimator to know which schema to use and update the state
// It will triggers a re-render to have the validation attributes updated
// which might be unnecessary as we are validating with zod anyway
// If the type looks like this:
type Shape = { a: string, c: string } | { b: string, c: number }
// It should treat it like this:
type Shape = { a?: string, b?: string, c: string | number } |
Beta Was this translation helpful? Give feedback.
2 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
I have a form that conditionally displays a group of fields based on a boolean value passed into my form component. If the value is true, these form fields are displayed and are required based on a zod schema. Otherwise, the fields are not displayed. The user can't change the value of this boolean. The form will not change once it's rendered.
It looks like the
constraint
property onuseForm
can't handle a zoddiscriminatedUnion
.ex:
I tried creating 2 different schemas; one with the extra data and one without, and conditionally passing them based on the
collectLeads
value, but TypeScript didn't like that.Do you have any suggestions for handling this use case? Should I make 2 different form components? I haven't checked to see if the
parse
function'sschema
property will work with a zoddiscriminatedUnion
but I'm thinking it will be okay which will allow me to use the same action function for both forms.Thanks
Great meeting you at Remix Conf, Edmund at Remix Conf. I hope you got some rest.
Beta Was this translation helpful? Give feedback.
All reactions