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), |
Expected Behaviour
Setting a field value to
undefinedeither sets the value toundefinedor removes the path to the value from the values.Actual Behaviour
Setting a field value to
undefineddoes nothing.Previous Reasoning
When an existing array in the form values is used through a
FormFieldto render nested array elements which also use any derivation of theFormFieldcomponent, removing items from the array itself becomes impossible. The childFormFieldwould set the field toundefinedbefore/instead of unmounting. Efficiently preventing any removals of the items in the array.Say we have a form which looks like this:
After you click the
<RemoveLast />the array would become:['John', undefined], instead of:['John'].Described in the following test:
domonda-js/packages/domonda-react-form/__tests__/FormField.test.tsx
Lines 330 to 383 in c18bc2f
and such behaviour is allowed by this segment:
domonda-js/packages/domonda-form/src/createFormField.ts
Lines 92 to 95 in c18bc2f