T
TanStack3w ago
adverse-sapphire

Reusable array component

Hi, I am trying to create reusable array component but it feels like i am doing something wrong. What is idiomatic way to do it? I am at this so far:
function ArrayTextField({placeholder}) {
const field = useFieldContext<Array<string>>()
const form = useFormContext()

return (
<div>
{
field.state.values.map((_, index) => {
<form.Field
key={index}
name={`${field.name}[${index}]`}>
{(subfield) => (
<Input
value={subfield.state.value}
onChange={(e) => {
subfield.handleChange(e.target.value)
}}
onBlur={subfield.handleBlur}
placeholder={placeholder}
/>
)}
</form.Field>
})
}
<button onClick={field.pushValue("")}>
Add
</button>
</div>
)
}
function ArrayTextField({placeholder}) {
const field = useFieldContext<Array<string>>()
const form = useFormContext()

return (
<div>
{
field.state.values.map((_, index) => {
<form.Field
key={index}
name={`${field.name}[${index}]`}>
{(subfield) => (
<Input
value={subfield.state.value}
onChange={(e) => {
subfield.handleChange(e.target.value)
}}
onBlur={subfield.handleBlur}
placeholder={placeholder}
/>
)}
</form.Field>
})
}
<button onClick={field.pushValue("")}>
Add
</button>
</div>
)
}
(I get string cannot be assigned to never on name property and string cannot be assigned to updaterFn<never,never> in onChange)
6 Replies
correct-apricot
correct-apricot3w ago
The form is typed as not having any fields, as this isn't really the intended usage of Form Composition besides, even if it were typed to have some sort of fields, TS would not be able to infer that ${field.name}[${field.index}] must exist
adverse-sapphire
adverse-sapphireOP3w ago
Is there better way to acomplish this?
correct-apricot
correct-apricot3w ago
Depends. With your current idea, you hide the logic of the fields behind the component, so you lose out on changing them later. Is that a problem, or do you already know what the configuration of the field is in advance?
adverse-sapphire
adverse-sapphireOP3w ago
I have similar array fields reused multiple times in my designs. So i know configuration and that they are stable. I need to change stuff like label and so but logic stays the same.
correct-apricot
correct-apricot3w ago
withFieldGroup could be the answer you're looking for. It allows the child fields to use AppField, which in turn means you can use field components. Rough draft of what it would look like:
const YourStringArray = withFieldGroup({
// what structure does it expect?
// -> a property with string[].
// it should be an actual runtime object so that
// Object.keys() receives it properly. The value is unused.
defaultValues: { array: [''] },
render: function Render({ group }) {
return <group.AppField name="array" mode="array">
{/* ... */}
</group.AppField>
}
})


// Usage
const form = useAppForm({
// ...
})

<YourStringArray form={form} fields={{
array: 'this.only.allows.string[].paths'
}} />
const YourStringArray = withFieldGroup({
// what structure does it expect?
// -> a property with string[].
// it should be an actual runtime object so that
// Object.keys() receives it properly. The value is unused.
defaultValues: { array: [''] },
render: function Render({ group }) {
return <group.AppField name="array" mode="array">
{/* ... */}
</group.AppField>
}
})


// Usage
const form = useAppForm({
// ...
})

<YourStringArray form={form} fields={{
array: 'this.only.allows.string[].paths'
}} />
adverse-sapphire
adverse-sapphireOP3w ago
Thanks i will try this.

Did you find this page helpful?