Anna Harshyna
Anna Harshyna

Reputation: 93

React Autocomplete with Material UI

I'm implementing a component Autocomplete using Material UI library.

But there's a problem - I'm not sure how to pass value and onChange properly, because I have a custom implementation of TextField that requires value and onChange as well. Should I pass value and onChange twice - to Autocomplete and TextField? Or maybe there's a better solution? Would appreciate any help! Here's my code:

import { Autocomplete as MuiAutocomplete } from '@material-ui/lab'
import { FormControl } from 'components/_helpers/FormControl'
import { useStyles } from 'components/Select/styles'
import { Props as TextFieldProps, TextField } from 'components/TextField'

export type Props = Omit<TextFieldProps, 'children'> & {
  options: Array<any>
  value: string
  onChange: (value: string) => void

  disabled?: boolean
}

export const Autocomplete = (props: Props) => {
  const classes = useStyles()

  return (
    <FormControl
      label={props.label}
      error={props.error}
      helperText={props.helperText}
    >
      <MuiAutocomplete
        options={props.options}
        // value={props.value}
        // onChange={event =>
        //   props.onChange((event.target as HTMLInputElement).value as string)
        // }
        classes={{
          option: classes.menuItem,
        }}
        disabled={props.disabled}
        getOptionLabel={option => option.label}
        renderInput={params => (
          <TextField
            {...params}
            placeholder={props.placeholder}
            value={props.value}
            onChange={props.onChange}
          />
        )}
        renderOption={option => {
          return <Typography>{option.label}</Typography>
        }}
      />
    </FormControl>
  )
}```

Upvotes: 4

Views: 7348

Answers (2)

Dhrupad Patel
Dhrupad Patel

Reputation: 61

import React, { useEffect, useState } from "react";
import { Autocomplete } from "@mui/material/node";
import { Controller, useFormContext } from "react-hook-form";
import { TextField } from "@mui/material";
import PropTypes from "prop-types";

 const valueFunc = (arr, id) => {
  const temp = arr.length > 0 && arr?.find((element) => element.id === id);
  return temp;
};

AutocompleteSearch.propTypes = {
  options: PropTypes.arrayOf({
    title: PropTypes.string,
    id: PropTypes.string,
  }),
  name: PropTypes.string,
};

export default function AutocompleteSearch({
  name,
  options,
  label,
  id,
  ...other
}) {
  const [temp, setTemp] = useState({});
  const { control, setValue } = useFormContext();

  useEffect(async () => {
    const found = valueFunc(options, id);
    await setTemp(found);
  }, [options, id]);

  return (
    <Controller
      control={control}
      name={name}
      rules={{ required: true }}
      render={({ fieldState: { error } }) => (
        <>
          <div >
            <Autocomplete
              id="controllable-states-demo"
              onChange={(_, v) => {
                setValue(name, v?.id);
                setTemp(v);
              }}
              onBlur={(e) => {
                e.target.value == "" && setValue(name, "");
              }}
              value={temp}
              options={options}
              getOptionLabel={(item) => (item.title ? item.title : "")}
              renderInput={(params) => (
                <>
                  <TextField
                    {...params}
                    label={label}
                    InputLabelProps={{
                      style: {
                        fontSize: "14px",
                        fontWeight: "400",
                        color: "#FF5B00",
                      },
                    }}
                 
                    size="small"
                    error={temp === null && !!error}
                    helperText={temp === null && error?.message}
                    {...other}
                  />
                </>
              )}
            />
          </div>
        </>
      )}
    />
  );
}
 <AutocompleteSearch
   name="pharmacy_group_title"
   label="Pharmacy Group" 
   options={pharmacyGroups}                           // Array  {id , title}   
   id={defaultValues?.pharmacy_group_title}           // ID
  />

Upvotes: 0

alex9552
alex9552

Reputation: 231

Material UI has props built in to handle the state of the Autocomplete vs input values.

You can see it in use in the docs here: https://material-ui.com/components/autocomplete/#controllable-states

In your example, you would want to add the inputChange and onInputChange props to the Autocomplete component. These will get passed down to your TextField through the params passed to the renderInput function.

So your final code would look something like the below snippet copied from the linked documentation:

<Autocomplete
  value={value}
  onChange={(event, newValue) => {
    setValue(newValue);
  }}
  inputValue={inputValue}
  onInputChange={(event, newInputValue) => {
    setInputValue(newInputValue);
  }}
  id="controllable-states-demo"
  options={options}
  style={{ width: 300 }}
  renderInput={(params) => <TextField {...params} label="Controllable" variant="outlined" />}
/>

Upvotes: 5

Related Questions