Search code examples
reactjsmaterial-uiformikformik-material-ui

Using Material UI's Autocomplete using Formik to display different value in dropdown but set different value in formik state


I am trying to use Material UI's Autocomplete with Formik. Here is a custom Autocomplete component I wrote to use with Formik.

import React from "react";
import Autocomplete from "@material-ui/lab/Autocomplete";
import TextField from "@material-ui/core/TextField";
import { fieldToTextField } from "formik-material-ui";

const FormikAutocomplete = ({ textFieldProps, ...props }) => {
  const {
    form: { setTouched, setFieldValue },
  } = props;
  const { error, helperText, ...field } = fieldToTextField(props);
  const { name } = field;

  return (
    <Autocomplete
      {...props}
      {...field}
      onChange={(_, value) =>
        setFieldValue(name, value)
      }
      onBlur={() => setTouched({ [name]: true })}
      renderInput={(props) => (
        <TextField
          {...props}
          {...textFieldProps}
          helperText={helperText}
          error={error}
        />
      )}
    />
  );
};

export default FormikAutocomplete;

Here is how components callled

<Field
  name="title"
  component={FormikAutocomplete}
  options={gender}
  getOptionLabel={(option) => option.title_name_long}
  textFieldProps={{
    label: "Title",
    required: true,
    variant: "outlined",
    margin: "dense",
  }}
/>

Now what I intend to do is: If I have a object like

gender=[{gender_name_short:"F",gender_name_long:"Female},{gender_name_short:"M",gender_name_long:"Male}]

I want the Autocomplete dropdown to show options male,female. But I want the formik state to save M,F respectively once selected from dropdown. Currently the entire object gets saved.

How can this be done?


Solution

  • In FormikAutocomplete component,

    • use setFieldValue in the onChange of autocomplete
    • use gender_name_long in getOptionLabel to display Male , Female
    • use gender_name_short in getOptionSelected to use M or F

    With that, finally, when you submit, you will get to see M/F not Male/Female

    Working demo

    const gender = [
      { gender_name_short: "F", gender_name_long: "Female" },
      { gender_name_short: "M", gender_name_long: "Male" }
    ];
    const validationSchema = object().shape({
      // genderObj: array().required("At least one gender is required")
    });
    
    const initialValues = {
      username: "abc",
      country: "",
      gender: "M",
      birthdate: null
    };
    
    const FormikAutocomplete = ({ textFieldProps, ...props }) => {
      const {
        form: { setTouched, setFieldValue }
      } = props;
      const { error, helperText, ...field } = fieldToTextField(props);
      const { name } = field;
    
      return (
        <Autocomplete
          {...field}
          {...props}
          onChange={(_, data) => {
            setFieldValue("gender", data.gender_name_short);
          }}
          onBlur={() => setTouched({ [name]: true })}
          // getOptionLabel={item => item.gender_name_long} //<----see here
          getOptionLabel={item => {
            // console.log( '====>' , typeof item === "string" ? props.options.find(i => i.gender_name_short === item).gender_name_long : item.gender_name_long)
            return typeof item === "string"
              ? props.options.find(i => i.gender_name_short === item)
                  .gender_name_long
              : item.gender_name_long;
          }}
          // getOptionLabel={item => typeof item === 'string' ? props.options.find(i => i.gender_name_short === item).gender_name_long : item.gender_name_long}
          getOptionSelected={(item, current) => {
            return item.gender_name_short === current;
          }}
          // defaultValue={'hi'}
          renderInput={props => (
            <TextField
              {...props}
              {...textFieldProps}
              helperText={helperText}
              error={error}
            />
          )}
        />
      );
    };
    
    const SimpleFormExample = () => (
      <div>
        <h1>Simple Form Example</h1>
        <Formik
          initialValues={initialValues}
          validationSchema={validationSchema}
          validateOnBlur={false}
          validateOnChange
          onSubmit={(values, { resetForm, setSubmitting }) => {
            console.log(values);
            resetForm();
            // setSubmitting(false);
          }}
        >
          {formik => (
            <Form>
              <Field
                name="gender"
                component={FormikAutocomplete}
                label="gender"
                options={gender}
                textFieldProps={{
                  fullWidth: true,
                  margin: "normal",
                  variant: "outlined"
                }}
                // multiple
              />
    
              <button type="submit">Submit</button>
            </Form>
          )}
        </Formik>
      </div>
    );
    
    export default SimpleFormExample;