Description
Summary
Autocomplete components should work in a regular html form submission, by accepting a name
prop and adding <input type="hidden" name={nameProp} value={value}/>
elements, when the value
prop is not being controlled.
If the multiple
prop is true
, then a list of hidden input elements should be added using field name array syntax (i.e. name={`${nameProp}[]`})
)
Docs should note that when using in uncontrolled mode, the name
prop should be added to the autocomplete component, and not manually added to the element returned by renderInput
(usually a TextField), to prevent duplicate entries in the form submission.
Ideally, there ought to be a mechanism to specify what the value
of the hidden input should be, based on the selected option(s). For example in an autocomplete I may want to show a list of movie names, concatenated with the year. When the user selects one, I likely want to send the movie id
in the form submission, not the option label.
Currently, I can create the hidden inputs manually fairly easily if it is a multi select, by adding these props:
multiple
renderTags={(selectedOptions, getTagProps) =>
selectedOptions.map((value, index) => (
<React.Fragment key={value.id}>
<Chip {...getTagProps({ index })} label={`${value.title}, ${value.year}`} />
<input type="hidden" name={`movie_id[]`} value={value.id} />
</React.Fragment>
)
)
}
Or if it is a single select, and the value I want as part of the form submission is the same as the option label, I can just add the name
prop to the TextField in renderInput
and I don't need a hidden input:
renderInput={(params) => (
<TextField
{...params}
name="movie"
label="Movie"
/>
)}
However, if it's a single select and the value I want as part of the form submission is not equal to the option label, I need to add the hidden input field to the renderInput
prop, and ensure the field name is attached to the hidden input, not the TextField input. Since the selected option
is not available in the params
arg of renderInput
, the only thing I can do is a reverse lookup to find the id
based on the option label (and hope there are not two movies with the same title and year in my dataset).
renderInput={(params) => (
<>
<TextField
{...params}
label="movie"
/>
<input
type="hidden"
name="movie_id"
value={getMovieId(params.inputProps.value) ?? ""}
/>
</>
)}
This is the only case where as far as I can tell with the current API it is impossible to achieve something satisfactory in user-land, and still use an uncontrolled component. If the renderInput
callback was provided the selected option as a second arg, we would at least be able to cover this base.
For completeness I note that if you use state, you can make even that last use-case work. However enabling the Autocomplete component to operate as an uncontrolled component as part of a form is a big enough win for simplicity for me to feel this feature request is warranted, (in many cases is has positive performance implications too).
Examples
/*
* This one renders a hidden input with initial value="" and name="movie_id"
* When the user selects an option, the value is updated to getOptionValue(option)
*/
const MyComponent = () => {
const options = useGetOptions()
return <Autocomplete
name="movie_id"
options={options}
getOptionLabel={option =>`${option.title}, ${option.year}`}
getOptionValue={option => option.id}
renderInput={params => (
<TextField
{...params}
label="Movie"
/>
)}
/>
}
/*
* This one renders one hidden input for each selected option with value=getOptionValue(option) and name="movie_id[]"
* When the user selects an option, the value is updated to getOptionValue(option)
*/
const MyMultiSelectComponent = () => {
const options = useGetOptions()
return <Autocomplete
name="movie_id"
options={options}
multiple
getOptionLabel={option =>`${option.title}, ${option.year}`}
getOptionValue={option => option.id}
renderInput={params => (
<TextField
{...params}
label="Movie"
/>
)}
/>
}
Motivation
With React Router data router patterns, (or Remix) we are able to simplify some aspects of writing forms in React apps by using the normal HTML form pattern. With autocomplete components this is tricky.
Search keywords: uncontrolled autocomplete