T
TanStack2mo ago
criminal-purple

How to type `form` ?

1. Parent component
// page.tsx
function Page() {
const form = useAppForm()

return (
<>
<Filters /> // need to pass `form` prop
</>
)
}
// page.tsx
function Page() {
const form = useAppForm()

return (
<>
<Filters /> // need to pass `form` prop
</>
)
}
2. Fetch data from useQuery
// filters.tsx
function Filters() {
const { data: filtersData } = useQuery();

return (
<>
{filterData.map((filter) => <Filter filter={filter} />)} // need to pass `form` prop
</>
)
}
// filters.tsx
function Filters() {
const { data: filtersData } = useQuery();

return (
<>
{filterData.map((filter) => <Filter filter={filter} />)} // need to pass `form` prop
</>
)
}
3. need form prop when use withForm
// filter.tsx
const Filter = withForm({
defaultValues: formOpts.defaultValues,
render: function Render({ form }) {
return <form.AppField>
{(field) => <div>

</div>}
</form.AppField>
}
});
// filter.tsx
const Filter = withForm({
defaultValues: formOpts.defaultValues,
render: function Render({ form }) {
return <form.AppField>
{(field) => <div>

</div>}
</form.AppField>
}
});
AppFieldExtendedReactFormApi is not available to import
No description
9 Replies
equal-aqua
equal-aqua2mo ago
Form Composition | TanStack Form React Docs
A common criticism of TanStack Form is its verbosity out-of-the-box. While this can be useful for educational purposes helping enforce understanding our APIs it's not ideal in production use cases. As...
dependent-tan
dependent-tan2mo ago
if you mean how to align withForm's expected type with your own, you should have a formOptions constant with shared data. That data includes: * defaultValues * validators
criminal-purple
criminal-purpleOP2mo ago
should I spread formOpts
No description
dependent-tan
dependent-tan2mo ago
yes
criminal-purple
criminal-purpleOP2mo ago
What i mean is should i wrap withForm in both Filters and Filter? Is this the right pattern?
const formOpts = {};

const Filter = withForm({
...formOpts,
props: {},
render: function Render({ form }) {
return (
<form.AppField name="fieldName">
{(field) => (
<input
value={field.state.value}
onChange={(e) => {
field.setValue(e.target.value);
form.handleSubmit();
}}
/>
)}
</form.AppField>
);
},
});

const Filters = withForm({
...formOpts,
render: function Render({ form }) {
return (
<div>
<Filter
form={form}
/>
</div>
);
},
});

// page.tsx
<Filters form ={form}/>
const formOpts = {};

const Filter = withForm({
...formOpts,
props: {},
render: function Render({ form }) {
return (
<form.AppField name="fieldName">
{(field) => (
<input
value={field.state.value}
onChange={(e) => {
field.setValue(e.target.value);
form.handleSubmit();
}}
/>
)}
</form.AppField>
);
},
});

const Filters = withForm({
...formOpts,
render: function Render({ form }) {
return (
<div>
<Filter
form={form}
/>
</div>
);
},
});

// page.tsx
<Filters form ={form}/>
because I used to do <FormProvider /> and useFormContext() in RHF
dependent-tan
dependent-tan2mo ago
sort of looks alright. I assume with ChildComponent you meant Filter, and with the handleSubmit call right after setting the value One thing I can recommend:
const Filter = withForm({
...formOpts,
props: {},
render: function Render({ form }) {
return (
<form.AppField
name="fieldName"
listeners={{
// let TSF handle the side effect for you
onChange: () => form.handleSubmit()
}}
>
{(field) => (
<input
value={field.state.value}
onChange={(e) => {
field.handleChange(e.target.value);
}}
/>
)}
</form.AppField>
);
},
});
const Filter = withForm({
...formOpts,
props: {},
render: function Render({ form }) {
return (
<form.AppField
name="fieldName"
listeners={{
// let TSF handle the side effect for you
onChange: () => form.handleSubmit()
}}
>
{(field) => (
<input
value={field.state.value}
onChange={(e) => {
field.handleChange(e.target.value);
}}
/>
)}
</form.AppField>
);
},
});
handleChange and setValue both do the same thing internally, but handleChange is clearer with its intent
criminal-purple
criminal-purpleOP2mo ago
OH nice! Haven't thought of that. I still think something similiar to this is neccessary. Sometimes, people got deeply nested form field. If you have to warp all of them with an HOC, we're going back to prop drilling issue.
dependent-tan
dependent-tan2mo ago
well, my honest opinion is that prop drilling is the only type safe way to go about it. However, you're right. There's also cases where you literally cannot pass it as prop (certain table layouts, routing files), so that's where a useTypedFormContext would come in handy it's not a big deal to implement, so perhaps after the array PR is done, I can quickly implement that
criminal-purple
criminal-purpleOP2mo ago
I get your point.

Did you find this page helpful?