0

I am trying to make a form builder which takes fields and form (from useForm) to simply map over the fields array and return input based on the type of field, and register the field to mantine form like {...form.getInputProps(name)} where form if the props send to FormBuilderComponent. But doing so I am unable to interact with the input fields. Please help me. here's the link to codesandbox. https://codesandbox.io/s/awesome-almeida-58e4nc?file=/src/App.tsx What am I missing?

const FormBuilder = ({ fields, form }) => {
  const theme = useMantineTheme();
  console.log(form);
  const mappedFields = fields.map((field) => {
    const {
      type,
      placeholder,
      name,
      label,
      options,
      col,
      pattern,
      required,
      errorMessage,
      minLength,
      maxLength
    } = field;

    if (type === "select") {
      return (
        <Select
          transition="pop-bottom-left"
          transitionDuration={80}
          transitionTimingFunction="ease"
          label={label}
          data={options}
          placeholder={placeholder}
          required={required}
        />
      );
    }

    if (type === "text") {
      return (
        <Grid.Col {...form.getInputProps(name)} span={col} key={randomId()}>
          <TextInput
            placeholder={placeholder}
            label={label}
            // required={required}
            {...form.getInputProps(name)}
          />
        </Grid.Col>
      );
    }

    if (type === "date-picker") {
      return (
        <Grid.Col span={col} {...form.getInputProps(name)} key={randomId()}>
          <DatePicker
            placeholder={placeholder}
            label={label}
            withAsterisk={required}
            // {...form.getInputProps(name)}
          />
        </Grid.Col>
      );
    }

    if (type === "switch") {
      return (
        <Grid.Col span={col} key={randomId()}>
          <Switch
            color="teal"
            size="md"
            label={label}
            // required={required}
            // checked
            thumbIcon={
              true === true ? (
                <IconCheck
                  size={12}
                  color={theme.colors.teal[theme.fn.primaryShade()]}
                  stroke={3}
                />
              ) : (
                <IconX
                  size={12}
                  color={theme.colors.red[theme.fn.primaryShade()]}
                  stroke={3}
                />
              )
            }
          />
        </Grid.Col>
      );
    }

    if (type === "number") {
      return (
        <Grid.Col span={col} key={randomId()}>
          <NumberInput
            placeholder={placeholder}
            label={label}
            withAsterisk={required}
            // minLength={minLength}
            // maxLength={maxLength}
          />
        </Grid.Col>
      );
    }
  });

  return <>{mappedFields}</>;
};
export default function App() {
  const form = useForm();
  return (
    <MantineProvider withGlobalStyles withNormalizeCSS>
      <form>
      <Grid>
        <FormBuilder fields={fields} form={form} />
       
        <Grid.Col span={12}>
          <Input placeholder='hy' {...form.getInputProps('ghost')} />
        </Grid.Col>
        <Grid.Col span={12}>
          <Group position='right' mt={'md'}>
            <Button type={'submit'}>Next</Button>
          </Group>
        </Grid.Col>
      </Grid>
    </form>
    </MantineProvider>
  );
}

I want to build a form with multiple input fields and want to read it's value and validate them from the parent component.

2 Answers2

0

It has nothing to do with mantine form. I was using { randomId } from "@mantine/hooks" to generate a unique key for mapped items. But it generates new key on every render so react was unable to keep track of changes.

Key must be unique and should persist across re-renders.

  • Since the form is passed on from another component ``, the state will always remain since the child component, ``, is not the holder of the state and therefore not managing the state. The form state will still be available even if the component `key` changes. – Live Software Developer Jan 03 '23 at 13:15
0

This is what am seeing in your code.

export default function App() {
  const form = useForm();
  return (
    ...
  );
}

There is a problem with the way you have done it and so the form won't work. That is you have created a form without initialValues. In other words, the form is empty. And also in your <FormBuilder /> builder component, you have not set any fields for the form but you are reading some fields from your form. A solution to this, is to this is to initialize the fields like

const form = useForm({
    initialValues: {
      email: '',
      termsOfService: false,
    },

    validate: {
      email: (value) => (/^\S+@\S+$/.test(value) ? null : 'Invalid email'),
    },
  });

As from the docs

Or use the form.setFieldValue method on the form builder which am not sure whether it will work. The most successful way is to initialize all the potential fields in your form before passing it into the FormBuilder component.

If you want to use the same fields you have in your fields array ie


const fields = [
    {
        name: "gender",
        ...
    },
    {
        name: "full_name",
        ...
    },
    {
        name: "date_of_birth",
        ...
    },
];


const initialValues = fields.reduce((acc, field) => {
    acc[field.name] = "";
    return acc;
}, {});

const form = useForm({
    initialValues: initialValues
})

This way, you will have the initial value based on the given fields for your form.