Search code examples
reactjsmaterial-uiformikyup

Formik material ui with autocomplete not works as expected


I'm using formik with material-ui to build custom autocomplete component.

my link to code

I add validation using yup for autocomplete to be required (user need to select one option):

 validationSchema: Yup.object().shape({
      skill: Yup.object().shape({
        value: Yup.string().required(),
        label: Yup.string().required()
      })
    }),

when user select any option I want it to save the option as {label,value} and not just the value because then I need to send it as object to the server.

I get some errors:

  1. when I press submit I get error

    Objects are not valid as a React child (found: object with keys {value, label}). If you meant to render a collection of children, use an array instead.

  2. I get those warnings:

    MUI: The value provided to Autocomplete is invalid. None of the options match with `{"label":"","value":""}`. You can use the `isOptionEqualToValue` prop to customize the equality test.

    Warning: Failed prop type: Invalid prop `helperText` supplied to `ForwardRef(TextField)`, expected a ReactNode.

I'm trying to build reusable autocomplete component to be able to get the selected value as {label,value} without any error and working as expected. I'm not sure if my validation is the right way. I want just the user to select an option.

in addition, I would like to ask, can I store object details into the value not as string? if no, what the best way to store value of object as option?

is my yup schema is the right way? should I store id of object as value and then filter it? how to pass value as string to autocomplete and show it selected in autocomplete? How can I do it without skill schema as object, just as string like:

 validationSchema: Yup.object().shape({
  skill: Yup.string().required()
}),

Solution

  • This is because initial value as {label:'' , value :''} is not in skills list. so in isOptionEqualToValue it will return false

    simply remove isOptionEqualToValue prop and in value check if there is option with that value and if not set null

      const getValueFromOptions = (value: string) => {
        return (
          options.find((option: any) => option.value === value) ?? null
        );
      };
    

    and in auto complete :

    <Autocomplete
         
          value={getValueFromOptions(field.value)}
        ...
    

    or check if field.value has value otherwise null

      <Autocomplete
             
          value={Boolean(field.value?.value) ? field.value : null}
        ...
    

    and another error was from show error message because meta.error is object that contains value and label error so you need to show one of them, I changed it to this :

    renderInput={(params) => {
    
            const errorMsg = meta.error as { value?:string }
    
            return (
              <TextField
                {...params}
                label={label}
                name={name}
                error={showError}
                helperText={meta.touched && errorMsg?.value}
              />
            );
          }}
    

    and another error was from show error message, it was displaying 'skill.value is a required field' so I simply add new message in yup validation

    validationSchema: Yup.object().shape({
          skill: Yup.object().shape({
            value: Yup.string().required('skill is a required field'),
            label: Yup.string()
          })
        }),
    

    you see pure code here : https://codesandbox.io/s/blissful-nova-nqvxq2?file=/src/components/AutocompleteField.tsx