Expected Behaviour
Setting a field value to undefined
either sets the value to undefined
or removes the path to the value from the values.
Actual Behaviour
Setting a field value to undefined
does nothing.
Previous Reasoning
When an existing array in the form values is used through a FormField
to render nested array elements which also use any derivation of the FormField
component, removing items from the array itself becomes impossible. The child FormField
would set the field to undefined
before/instead of unmounting. Efficiently preventing any removals of the items in the array.
Say we have a form which looks like this:
const values = { people: ['John', 'Jane'] };
const tree = (
<Form defaultValues={values}>
<FormField path="people">
{(array, setArray) => (
<>
{array.map((_0, index) => (
<FormField path={`people[${index}]`}>
{(item) => (
<input manipulate={item} />
)}
</FormField>
))}
<RemoveLast onClick={() => setArray(array.pop())} />
</>
)}
</FormField>
</Form>
);
After you click the <RemoveLast />
the array would become: ['John', undefined]
, instead of: ['John']
.
Described in the following test:
|
it('should not render or set undefined values on array fields', (done) => { |
|
interface DV { |
|
people: string[]; |
|
} |
|
|
|
const dv: DV = { |
|
people: ['John', 'Foo', 'Bar'], |
|
}; |
|
|
|
let form: DomondaForm<DV>; |
|
const { rerender } = render( |
|
<Form getForm={(f) => (form = f)} resetOnDefaultValuesChange defaultValues={dv}> |
|
<FormField<any[]> path="people"> |
|
{({ value }) => ( |
|
<> |
|
{value.map((person: any, index) => ( |
|
<FormField key={person} path={`people[${index}]`}> |
|
{() => null} |
|
</FormField> |
|
))} |
|
</> |
|
)} |
|
</FormField> |
|
</Form>, |
|
); |
|
|
|
// @ts-ignore because form should indeed be set here |
|
if (!form) { |
|
throw new Error('form instance should be set here!'); |
|
} |
|
|
|
const nextDv: DV = { |
|
people: ['Foo'], |
|
}; |
|
|
|
rerender( |
|
<Form resetOnDefaultValuesChange defaultValues={nextDv}> |
|
<FormField<any[]> path="people"> |
|
{({ value }) => ( |
|
<> |
|
{value.map((person: any, index) => ( |
|
<FormField key={person} path={`people[${index}]`}> |
|
{() => null} |
|
</FormField> |
|
))} |
|
</> |
|
)} |
|
</FormField> |
|
</Form>, |
|
); |
|
|
|
expect(form.state.values).toEqual(nextDv); |
|
done(); |
|
}); |
and such behaviour is allowed by this segment:
|
values: |
|
rest.value === undefined |
|
? state.values |
|
: setWith(() => undefined, path, rest.value, form.state.values), |