Skip to content

react-form: setting value to undefined #31

@enisdenjo

Description

@enisdenjo

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),

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions