DevBF
DevBF

Reputation: 257

Passing Formik input values in nested forms using Material UI

I'm currently having problems when using Formik with MaterialUI forms. Specifically, I am having trouble passing Formik input values in nested forms using Material UI and having a small issue where Formik.handleChange is changing the value from number to string.

I have multiple forms that are split with Stepper component in Material UI. Since I am still learning, I tried to wrap Formik on one of the steps (later on I am going to wrap the whole stepper). Here's how it looks:

          {activeStep === 0 && (
            <Formik
              initialValues={initialValues}
              validationSchema={validationSchema}
              onSubmit={(values, { setSubmitting }) => {
                setTimeout(() => {
                  alert(JSON.stringify(values, null, 2));
                  setSubmitting(false);
                }, 400);
              }}
            >
              {formik => (
                <form onSubmit={formik.handleSubmit}>
                  <div className="col-xl-6">
                    <Portlet fluidHeight>
                      <PortletHeader title="Incident Type" />
                      <PortletBody>
                        <IncidentSelect formik={formik} />
                      </PortletBody>
                    </Portlet>
                  </div>
                </form>
              )}
            </Formik>
          )}

The problem is inside the IncidentSelect form, Formik handleChange does not seem to change the selected radioButton. When I inspected with React Developer Tools in Chrome it seems that Formik.handleChange is changing the value from 0 to "0". How do I fix this?

Also, following the tutorial, I'm unsure how I can abstract my components? Note that DateTimePicker is using material-ui/pickers. I'm not sure how I am going to pass the value to Formik.

Any help is appreciated.

Thanks

function IncidentSelect(props) {
  const [value, setValue] = React.useState("female");
  const handleRadioChange = e => {
    console.log(props.formik.getFieldProps("incidentType"));
    setValue(e.target.value);
  };

  return (
    <>
      <FormControl component="fieldset">
        <FormLabel component="legend" required>
          Incident Type:
        </FormLabel>
        <RadioGroup
          aria-label="Incident Type"
          name="incidentType"
          value={value}
          onChange={handleRadioChange}
          {...props.formik.getFieldProps("incidentType")}
        >
          <FormControlLabel
            value={0}
            control={<Radio />}
            label="Injury To Guest"
          />
          <FormControlLabel
            value={1}
            control={<Radio />}
            label="Injury To Worker"
          />
          <FormControlLabel
            value={2}
            control={<Radio />}
            label="Incident / Accident"
          />
          <FormControlLabel
            value={3}
            disabled
            control={<Radio />}
            label="OSH / Kids Camp"
          />
        </RadioGroup>
      </FormControl>
      <DateTimePicker
        label="Signed Date"
        variant="outlined"
        className={classes.margin}
        value={selectedDate}
        onChange={handleDateChange}
      />
    </>
  );
}

Upvotes: 2

Views: 4851

Answers (1)

Robert Tirta
Robert Tirta

Reputation: 2911

As stated in the tutorial, it is easier to abstract the component that you want to use. Giving you chances to reuse them later in your application and more readable code.

Formik provides you with useFields API to get the props of the field via hooks. useFields is looking for the name props of your component to find the corresponding field.

Thus RadioGroup from MaterialUI can be extracted as follows:

export const IncidentRadioButton = ({ options, ...props }) => {
  const [field, meta] = useField(props);
  return (
    <>
      <RadioGroup {...field} {...props}>
        {options.map((option, index) => (
          <FormControlLabel
            control={<Radio />}
            label={option.name}
            value={option.value}
            key={index}
          />
        ))}
      </RadioGroup>
      {meta.touched && meta.error ? (
        <div className="error">{meta.error}</div>
      ) : null}
    </>
  );
};

Then you can use the options prop to put your data accordingly

Upvotes: 1

Related Questions