Closed crutchcorn closed 1 year ago
Currently, with the introduction of nested field support, we're able to support array-style fields something like the following:
<Field name="test[0].name"> // ... </Field> <Field name="test[1].name"> // ... </Field>
This should work, but there is a problem with this; when submitting a form, it will not treat test as an array but rather as an object:
test
{test: {0: {name: "val"}, 1: {name: "val"}}}
It would be nice to add a better story for field arrays in HouseForm.
Further, it would also be nice to have the ability to validate these field arrays using Zod's z.array() type to provide validation for these values.
Zod
z.array()
There are multiple different APIs we could use to achieve this effect.
<Field>
<FieldArray name="people" onChangeValidate={z.array(z.any()).min(3, "You must have three items in your array"}> {({ fields, setFields }) => ( <> {fields.map((field, index) => ( <Field key={field.key} name={`people.${index}.username`}> {({ value, errors, setValue }) => ( <div> <input value={value} onChange={e => setValue(e.target.value)} placeholder="Username" /> {errors.map(error => <span key={error}>{error}</span>} </div> )} </Field> ))} <button onClick={() => setFields([...fields, { name: "" }])}></button> </> )} </FieldArray>
Pros:
Cons:
FormContext
ArrayContext
setField
Field
listenTo
<FieldArray name="people" onChangeValidate={z.array(z.any()).min(3, "You must have three items in your array"}> {({errors, fields, setFields}) => ( <> {fields.map((_, index) => ( <ArrayField key={fields.key} name={`people.${index}.username`} onChangeValidate={z.string()}> {({value, setValue, errors}) => ( <div> <input value={value} onChange={e => setValue(e.target.value)} placeholder="Username" /> {errors.map(error => <span key={error}>{error}</span>} </div> )} </ArrayField> ))} <button onClick={() => setFields([...fields, { name: "" }])}></button> </> )} </FieldArray>
Notes:
field.key
setFields
<FieldArray name="people" onChangeValidate={z.array(z.any()).min(3, "You must have three items in your array"}> {({fields, add, remove, insert, move}) => ( <> {fields.map((_, index) => ( <ArrayField key={index} name={`people.${index}.username`} onChangeValidate={z.string()}> {({value, setValue, errors}) => ( <div> <input value={value} onChange={e => setValue(e.target.value)} placeholder="Username" /> {errors.map(error => <span key={error}>{error}</span>} </div> )} </ArrayField> ))} <button onClick={() => add({ name: "" })}>Add</button> <button onClick={() => remove(-1)}>Remove last</button> <button onClick={() => insert(3, {name: ""})}>Insert at third</button> <button onClick={() => move({from: 0, to: 1})}>Move the first array item to the second place</button> </> )} </FieldArray>
This is the winner
Work is being done on this branch:
feat/field-array
Currently, with the introduction of nested field support, we're able to support array-style fields something like the following:
This should work, but there is a problem with this; when submitting a form, it will not treat
test
as an array but rather as an object:It would be nice to add a better story for field arrays in HouseForm.
Further, it would also be nice to have the ability to validate these field arrays using
Zod
'sz.array()
type to provide validation for these values.Potential APIs
There are multiple different APIs we could use to achieve this effect.
Option 1: Reusing
<Field>
sPros:
Cons:
FormContext
andArrayContext
?)setField
but also create a newField
instance?Field
to updateArrayContext
with errors and morelistenTo
? How to link all related field elements?Option 2: Use dedicated components
Notes:
field.key
is a unique ID within the form to discourage people from using the array indexPros:
Cons:
setFields
logic - run into issue with reordering fields and losing values, etcOption 3: Use dedicated components with helper functions
This is the winner
Other Implementations