T
TanStack2w ago
like-gold

Sharing form type to custom hooks for cleaner code

Hey folks, I have read the philosophy of forms and previous discussion on why generic are bad. But I do want to ask what could go wrong if setup the form this way
export const { useAppForm , … } = creatFormHook({ … }):

function useSetupAppForm(props: FormSetupProps) {

return useAppForm({ ….formOptions, onSubmit, … }) // pass static values for TS inference
}

type AppForm = ReturnType<typeof useSetupAppForm> // <—— is this going to be problematic


function MyForm(props: FormSetupProps) {

Const form = useSetupAppForm(props);

useFormNavigation(form) // <— pass form to a custom hook and avoid cluttering the MyForm
}
export const { useAppForm , … } = creatFormHook({ … }):

function useSetupAppForm(props: FormSetupProps) {

return useAppForm({ ….formOptions, onSubmit, … }) // pass static values for TS inference
}

type AppForm = ReturnType<typeof useSetupAppForm> // <—— is this going to be problematic


function MyForm(props: FormSetupProps) {

Const form = useSetupAppForm(props);

useFormNavigation(form) // <— pass form to a custom hook and avoid cluttering the MyForm
}
9 Replies
ambitious-aqua
ambitious-aqua2w ago
not really. The 'generics are bad' comment in the philosophy page exists for two reasons: 1. We don't expect you to type all of them. There are many right now, with more to come. You shouldn't explicitly type them as TypeScript requires either all of them or none.
+ typeof useSetupAppForm
Works because TypeScript can infer from the passed properties of the form.
All generics are inferred.

- return useAppForm<T>()
TypeScript now assumes it must explicitly type *all* generics.
You will have a lot of trouble typing this correctly
+ typeof useSetupAppForm
Works because TypeScript can infer from the passed properties of the form.
All generics are inferred.

- return useAppForm<T>()
TypeScript now assumes it must explicitly type *all* generics.
You will have a lot of trouble typing this correctly
2. Generics can change in the future We may add more generics in the future. It recently increased from 10 to 12, which we don't consider to be breaking changes (see the TypeScript page). If your wrapper were to explicitly type all generics, you will have trouble upgrading it to newer versions. Your solution appears to comply with both of these, so it should be fine.
like-gold
like-goldOP2w ago
@Luca | LeCarbonator thanks for the quick response I appreciate it. It seems a lot of folks do want this flexibility of having access to their appForm in a custom hook, do you think some sort of official docs guidance would be helpful?
ambitious-aqua
ambitious-aqua2w ago
it depends on what the planned use cases are. Most users want that flexibility because they want 'presets' of certain values. formOptions is intended to give you a type safe way to implement that. Could you elaborate on your use case for this custom hook? Perhaps it can give insight into what users expect to be able to use ootb even an idealized API works. It doesn't need to comply with what's currently possible, just to give an idea of what you'd like to be able to do
like-gold
like-goldOP2w ago
Currently my main form component has gotten quite big, and hooks are a great way to break down such complexity and keep component clean. One of the hook manages the wizard nature of the overall form, and frequently needs access to formstate to decide if they user can jump to next section or not. I notice part of the reason withForm hook exists is to provide the form to a given component in a type safe manner. A similar API that helps get the type safe form to a custom hook might be nice. But maybe I am glossing over some basics here.
ambitious-aqua
ambitious-aqua2w ago
I see ... so it's not really the reusability part that you're looking for, but more being able to extract the hook to somewhere else so that the component is less complex
like-gold
like-goldOP2w ago
Exactly !
ambitious-aqua
ambitious-aqua2w ago
in the example you provided, what would be an example of FormSetupProps? Which parts would you leave up to the component using it?
like-gold
like-goldOP2w ago
FormSetupProps are some application specific prop that my host app sends to this form component, I am keeping this component MyForm uncontrolled so that it does everything including setting up formOptions with schema and finally calls a callback in FormSetupProps.onSubmit to do the final save to the server.
ambitious-aqua
ambitious-aqua2w ago
oh, I just noticed that you also have a LOC with using form as parameter for other functions. There are some helper types you can use for that, but they're rather limited. As mentioned above, TypeScript doesn't like partial inference for generics. Would these types help out? * AnyFormApi * AnyFieldApi * Any<X>? Something that doesn't exist yet? I suppose the current withForm is only intended for rendering. So a higher order function that creates the form type for you would be ideal, then? a downside with something like withForm is that it's very strict. If you tell it to use a form of a specific shape, then even minor changes can make the function unuseable for that changed form.

Did you find this page helpful?