Example of nice API: react-final-form-arrays
Coming to this library from react-final-form, there are a lot of things I miss from it. I really liked how clean, consistent, and extensible it was. One of the extensions to it was react-final-form-arrays (also see this article).
This provides a really, really nice API for adding and removing new elements to/from an array of fields:
fields.remove(index)
fields.push({ firstName: '', lastName: '' }
<FieldArray name="customers">
{({ fields }) => (
<div>
{fields.map((name, index) => (
<div key={name}>
<div>
<label>First Name</label>
<Field name={`${name}.firstName`} component="input" />
</div>
<div>
<label>Last Name</label>
<Field name={`${name}.lastName`} component="input" />
</div>
<button type="button" onClick={() => fields.remove(index)}>
Remove
</button>
</div>
))}
<button
type="button"
onClick={() => fields.push({ firstName: '', lastName: '' })}
>
Add
</button>
</div>
)}
</FieldArray>
Compared to current API
Compare that with the current recommended way to add/remove fields in an array, which is very verbose and boilerplatey:
const add = () => {
$form.users = $form.users.concat({ name: "", email: "" });
$errors.users = $errors.users.concat({ name: "", email: "" });
};
const remove = i => () => {
$form.users = $form.users.filter((u, j) => j !== i);
$errors.users = $errors.users.filter((u, j) => j !== i);
};
Not only is it boilerplatey, it exposes and requires knowledge of the internals of the library. And end-users of this library should not have to do all that work. And it's very easy to get one of those functions wrong. In fact, even this official example is not quite correct, because it fails to also modify $touched.users
!
Bug in doc example
Here is a sandbox (based on https://svelte-forms-lib-sapper-docs.now.sh/array) showing how $touched.users
should be modified too.
If you comment those 2 statements out and don't modify $touched.users
when you add/remove a user, it creates a bug where if you add a new user, $isValid
(initially true
because it starts with 0 users) does not change from true
to false
. As soon as you add an empty user to the array, it should be considered invalid because name
is a required field.
Related: a single store with form state instead of 3?
Ideally, I think it would be nice if we didn't have to try to keep 3+ objects ($form, $touched, $errors) in sync. Maybe there could be a centralized store of form data, keyed by field path, and under each field would be the state (value, touched, errors) for that field? I would highly recommend checking out how final-form manages state, since that is a very well-written library. (I haven't yet checked how Erik did it there but I would like to.)
Describe the solution you'd like
What I would like to see is a <FieldArray>
component added to this library that works similarly to the one in
react-final-form, and provides an API that is so enjoyable, simple, and easy to use that users literally can't mess up their add/remove functions like they can with the current API.
fields.remove(index)
fields.push({ firstName: '', lastName: '' }