T
TanStack4d ago
extended-salmon

possible to use withForm with 2 almost identical schemas

I have 2 zod schemas who are almost identical. i made this quick example https://codesandbox.io/p/devbox/immutable-water-5n6qst you can see on page1 and page2 the differences. One has the last_name defined as required and one has the last_name as optional. Is this somewhat possible? In real life i have 2 really big form which are basically the same but some fields are required in one and not in the other one. But the fields are all the same, this should not intervene with any type safety?
11 Replies
broad-brown
broad-brown3d ago
withForm is very strict, as its purpose is not to share common fields between forms. It expects a specific form so you can spread your large form component into multiple smaller ones. As for reusing sections of fields between multiple forms, withFieldGroup would be the better approach for that.
extended-salmon
extended-salmonOP3d ago
im looking into withFIeldGroup now but it throws the same error i updated the example
broad-brown
broad-brown3d ago
alright, will check it in a moment
extended-salmon
extended-salmonOP9h ago
No problem. @Luca | LeCarbonator Did you had any change to have look?
broad-brown
broad-brown9h ago
oh, apologies! I've forgotten. I have time now though, so I'll open it right away
extended-salmon
extended-salmonOP9h ago
No problem, if no luck i will just use the least explicit schema and set the other errors with setErrorMap with the more explicit schema I think that should work
broad-brown
broad-brown9h ago
Okay, couple things to note: 1. Runtime values are not used for withForm. However, withFieldGroup needs to know what keys are expected so it can create maps for you. That means you should pass it a defaultValues object that contains the shallow keys (and any value, those are actually unused)
export const UserFields = withFieldGroup({
// Dangerous! Can throw undefined errors at runtime
defaultValues: {} as z.input<typeof schema1>,
// ...
})

// Both type-safe and runtime-safe
const userFields: z.input<typeof schema1> = {
first_name: '',
last_name: ''
}

export const UserFields = withFieldGroup({
defaultValues: userFields,
// ...
})
export const UserFields = withFieldGroup({
// Dangerous! Can throw undefined errors at runtime
defaultValues: {} as z.input<typeof schema1>,
// ...
})

// Both type-safe and runtime-safe
const userFields: z.input<typeof schema1> = {
first_name: '',
last_name: ''
}

export const UserFields = withFieldGroup({
defaultValues: userFields,
// ...
})
2. Not all extended schemas implement it correctly You have two values that expect a certain structure: * first_name: string * last_name: string schema1 implements this correctly. However, look at the definitions of schema2: * first_name: string Okay! * last_name: string | undefined Broader than just string That's why it refuses to allow you to map last_name for schema2. you can go the other way though. If you want the reusable fields to be optional, then type them as such. Inside withFieldGroup, you'll have to manage the undefined state. that allows both required and optional fields to reuse them 3. TanStack Form is controlled input focused, so since your default values of the form are all undefined, React will throw errors on initial changes. The way to fix this is by giving it some default values. https://codesandbox.io/p/devbox/cocky-julien-7ghzqh here's a fork with the changes applied
extended-salmon
extended-salmonOP9h ago
Thanks will have a look in to it in a minute, having a meeting now. The problem is that this is actually a huge form with totally different data in real life, One form is basically a form to create a template (which has some data missing) and the other form is to implement the template where that data is required. So initially these optional fields are not there in the form at all but they are in the second. These optional fields are implemented per form basis but the required fields in both forms are in reusable parts. These reusable parts do get the whole schema though since its quite huge.
broad-brown
broad-brown9h ago
as long as the fields it expects and shares are required by both, it should be no problem the problem happens if you want to make a field reusable that's required by one form and optional in the other
extended-salmon
extended-salmonOP8h ago
yes thats the problem, i have these quite huge schemas (zod objects shared between frontend and backend) that i use to implement this form. the reusable parts are using this whole request, i just implement a small portion of it in the actual component and handle the rest outside. but in one request a field is optional. im looking into your example now I think instead of using the complete schema in these reusable components i only need to use the fields it actually implements and use your 'fix' for the optional fields
broad-brown
broad-brown7h ago
yeah, field groups fields should either be added if rendered fields within need it (changing a should change b, or similar), or if the field itself will be rendered

Did you find this page helpful?