Join GitHub today
GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.
Sign upGitHub is where the world builds software
Millions of developers and companies build, ship, and maintain their software on GitHub — the largest and most advanced development platform in the world.
[Autocomplete] Support values other than raw options #23708
Comments
@dantman There is a 4th argument to the onChange event that allows handling this problem. It looks like there is an opportunity to better document it. What do you think about this change? diff --git a/packages/material-ui/src/useAutocomplete/useAutocomplete.d.ts b/packages/material-ui/src/useAutocomplete/useAutocomplete.d.ts
index 0dfea8c799..47be1bd484 100644
--- a/packages/material-ui/src/useAutocomplete/useAutocomplete.d.ts
+++ b/packages/material-ui/src/useAutocomplete/useAutocomplete.d.ts
@@ -250,6 +250,7 @@ export interface UseAutocompleteProps<
* @param {object} event The event source of the callback.
* @param {T|T[]} value The new value of the component.
* @param {string} reason One of "create-option", "select-option", "remove-option", "blur" or "clear".
+ * @param {string} [details]
*/
onChange?: (
event: React.SyntheticEvent, it will then automatically update the API: diff --git a/docs/pages/api-docs/autocomplete.md b/docs/pages/api-docs/autocomplete.md
index 34caa37fa2..41f18c5120 100644
--- a/docs/pages/api-docs/autocomplete.md
+++ b/docs/pages/api-docs/autocomplete.md
@@ -67,7 +67,7 @@ The `MuiAutocomplete` name can be used for providing [default props](/customizat
| <span class="prop-name">loadingText</span> | <span class="prop-type">node</span> | <span class="prop-default">'Loading…'</span> | Text to display when in a loading state.<br>For localization purposes, you can use the provided [translations](/guides/localization/). |
| <span class="prop-name">multiple</span> | <span class="prop-type">bool</span> | <span class="prop-default">false</span> | If `true`, `value` must be an array and the menu will support multiple selections. |
| <span class="prop-name">noOptionsText</span> | <span class="prop-type">node</span> | <span class="prop-default">'No options'</span> | Text to display when there are no options.<br>For localization purposes, you can use the provided [translations](/guides/localization/). |
-| <span class="prop-name">onChange</span> | <span class="prop-type">func</span> | | Callback fired when the value changes.<br><br>**Signature:**<br>`function(event: object, value: T \| T[], reason: string) => void`<br>*event:* The event source of the callback.<br>*value:* The new value of the component.<br>*reason:* One of "create-option", "select-option", "remove-option", "blur" or "clear". |
+| <span class="prop-name">onChange</span> | <span class="prop-type">func</span> | | Callback fired when the value changes.<br><br>**Signature:**<br>`function(event: object, value: T \| T[], reason: string, details?: string) => void`<br>*event:* The event source of the callback.<br>*value:* The new value of the component.<br>*reason:* One of "create-option", "select-option", "remove-option", "blur" or "clear". |
| <span class="prop-name">onClose</span> | <span class="prop-type">func</span> | | Callback fired when the popup requests to be closed. Use in controlled mode (see open).<br><br>**Signature:**<br>`function(event: object, reason: string) => void`<br>*event:* The event source of the callback.<br>*reason:* Can be: `"toggleInput"`, `"escape"`, `"select-option"`, `"remove-option"`, `"blur"`. |
| <span class="prop-name">onHighlightChange</span> | <span class="prop-type">func</span> | | Callback fired when the highlight option changes.<br><br>**Signature:**<br>`function(event: object, option: T, reason: string) => void`<br>*event:* The event source of the callback.<br>*option:* The highlighted option.<br>*reason:* Can be: `"keyboard"`, `"auto"`, `"mouse"`. |
| <span class="prop-name">onInputChange</span> | <span class="prop-type">func</span> | | Callback fired when the input value changes.<br><br>**Signature:**<br>`function(event: object, value: string, reason: string) => void`<br>*event:* The event source of the callback.<br>*value:* The new value of the text input.<br>*reason:* Can be: `"input"` (user input), `"reset"` (programmatic change), `"clear"`. | However, in your case, the simplest is probably to store the value as-is, and derive it with a map when you need to transform it. Why not use this approach? |
Forcing the developer to manually manage the values list doesn't feel like a great alternative. Using Select, TextField, etc... isn't this complex even when using the
You mean store Honestly it feels wrong to have localized labels in form data just because of limitations in useAutocomplete's implementation. And MUI doesn't work this way with any other field. Select, Radio, and Checkbox can easily be connected to form handling code and all support using a separate value and label for options. It doesn't feel right that if you have a Select implemented that way (with the same simple submit handler code as the rest of your fields) and it becomes too long to be a normal select, you have to rewrite the submit handler code to work differently than every other field, just because the field type used changed from Select to Autocomplete. |
We have a similar pain in this direction on #21489. Ok, why not. we could:
Feel free to work on it. At least, we have spotted a small issue with the generated documentation. |
Summary💡
Right now (at least if you use
multiple
) Autocomplete/useAutocomplete thevalue
that comes back inonChange
is always the raw option data. i.e. If you useoptions=[{value: 'a', label: 'A'}, {value: 'b', label: 'B'}]
and select the "A" option then value will be[{value: 'a', label: 'A'}]
.Ideally it would be possible to provide
multiple=true options=[{value: 'a', label: 'A'}, {value: 'b', label: 'B'}]
and get back values like['a']
. Autocomplete does feel like this is intended to be supported, because you can provide agetOptionSelected
and it's used to identify selected options instead of doing any comparisons on the option itself. However when it comes to adding an item to amultiple
array, all useAutocomplete does isnewValue.push(option);
. There is no way to control what part of option is actually used as the value.I think a
getOptionValue(option)
function would fit the current options implementation the most.Examples🌈
Motivation🔦
MUI's
<Select>
easily supports this. It uses children instead, so all it takes isoptions.map(item => <MenuItem key={item.value} value={item.value}>{item.value}</MenuItem>)
to do this in Select and pretty much every project I've worked on uses Select this way. It would be strongly preferred if it were easy to use useAutocomplete the same way.