T
TanStack•2mo ago
optimistic-gold

Help with building forms with dynamic fields

Hi, I'm trying to use tanstack-form to help build a form that has dynamic elements but struggling. I have an API that returns data for the fields that looks like:
const formFields = [
{
"fieldName": "transfer.fee",
"description": "Transfer Fee"
"currentValue": "0.01"
},
{
"fieldName": "threshold",
"description": "Threshold"
"currentValue": "20"
},
// e.t.c
]
const formFields = [
{
"fieldName": "transfer.fee",
"description": "Transfer Fee"
"currentValue": "0.01"
},
{
"fieldName": "threshold",
"description": "Threshold"
"currentValue": "20"
},
// e.t.c
]
and later in the component
const form = useForm({
defaultValues: {
summary: '', // field that always exists
...formFields.reduce((acc, field) => {
acc[field.fieldName] = field.currentValue
return acc
}, {} as Record<string, string>)
}
onSubmit: async ({ value }) => {
// value here contains the dynamic fields but checking the type just shows summary.
console.log('Form submitted:', value)
},

})

// ....

{formFields.map((fieldConfig) => (
<form.Field
key={fieldConfig.fieldName}
name={fieldConfig.fieldName} // Type 'string' is not assignable to type '"summary"'.
children={(field) => (
<div>
<label>
{fieldConfig.description}
</label>
<input
name={field.name}
value={field.state.value}
onBlur={field.handleBlur}
onChange={(e) => field.handleChange(e.target.value)}
/>
</div>
)}
/>
))}
const form = useForm({
defaultValues: {
summary: '', // field that always exists
...formFields.reduce((acc, field) => {
acc[field.fieldName] = field.currentValue
return acc
}, {} as Record<string, string>)
}
onSubmit: async ({ value }) => {
// value here contains the dynamic fields but checking the type just shows summary.
console.log('Form submitted:', value)
},

})

// ....

{formFields.map((fieldConfig) => (
<form.Field
key={fieldConfig.fieldName}
name={fieldConfig.fieldName} // Type 'string' is not assignable to type '"summary"'.
children={(field) => (
<div>
<label>
{fieldConfig.description}
</label>
<input
name={field.name}
value={field.state.value}
onBlur={field.handleBlur}
onChange={(e) => field.handleChange(e.target.value)}
/>
</div>
)}
/>
))}
7 Replies
optimistic-gold
optimistic-goldOP•2mo ago
Issues i'm having: 1. I'm dynamically building the defaultValues in useForm. Is this the right way to do this? I think not because of point 2 2. I noticed on submit, the data sent contains the dynamic data but the type thinks summary is the only thing there. 3. Where I render the field and set the name, I see Type 'string' is not assignable to type '"summary"'. which means the dynamic fields are not working the way I want them to. 4. I noticed the value of the field only renders for names that have no dots. e.g threshold value in the input field renders fine but transfer.fee does not. I'll appreciate any help on how to build a form like this 🥺
optimistic-gold
optimistic-goldOP•2mo ago
Here's the full component
other-emerald
other-emerald•2mo ago
this is your problem
No description
other-emerald
other-emerald•2mo ago
TypeScript fails to see that you have a string index try extracting defaultValues like so:
const defaultValues: {
summary: string;
[key: string]: string;
} = {
// ...
}
const defaultValues: {
summary: string;
[key: string]: string;
} = {
// ...
}
optimistic-gold
optimistic-goldOP•2mo ago
Thanks @Luca | LeCarbonator , that did the trick for the types. How do i solve problem 4 ? value={field.state.value} <- this breaks when the key is something with dot notation
other-emerald
other-emerald•2mo ago
well, the name transfer.fee implies it's of structure
{ transfer: { fee: ? } }
{ transfer: { fee: ? } }
TypeScript doesn't really let you have strings with reserved characters, so it won't warn you. I also don't think it's written in the documentation, merely implied try to avoid the characters ., [ and ] also if you know a place in the docs where this ought to be mentioned, let me know
optimistic-gold
optimistic-goldOP•2mo ago
I see, I should probably switch to kebab case for all the keys then Much appreciated Luca, saved me a lot of headache here! I have other questions but will create a separate post when i get there 🙂

Did you find this page helpful?