-
-
Notifications
You must be signed in to change notification settings - Fork 32.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Autocomplete] Provide more details in the onChange event #19064
Comments
@whuey1992 What's your use case? |
I am using two autocompletes for a form. When certain options are selected in one autocomplete, it requires options from the other autocomplete to be selected. Same with if those options are removed. |
When a new option is selected I can check the last element added to |
@whuey1992 Thanks for sharing. It's unclear what a good solution to the problem would be. I would propose that we wait for more feedback before taking action. In your case, I don't understand what prevents you to use the final state to run the logic. |
It doesnt prevent me from running the logic, but it adds complexity to do a workaround (possibility of bugs) and isn't optimal O(AB) to do diffing. |
@whuey1992 Ok thanks for the feedback. I'm closing. From what you are describing, you are trying to bridge a gab between a declarative world and an imperative one. I would suggest that you check how you could remove this imperative requirement. I have added the |
On a side note, the fix on our side would be easy. So it really boils down to encouraging stateless logic. |
I have changed my mind. It seems to be a feature frequently present in alternative projects. In #18976, we see a potential use case. So, in this context, I think that we should push it further. |
Maybe something like this? diff --git a/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js b/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js
index de0bfd292..091a1b549 100644
--- a/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js
+++ b/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js
@@ -418,35 +418,41 @@ export default function useAutocomplete(props) {
}
};
- const handleValue = (event, newValue) => {
+ const handleValue = (event, newValue, reason, details) => {
if (value === newValue) {
return;
}
if (onChange) {
- onChange(event, newValue);
+ onChange(event, newValue, reason, details);
}
setValue(newValue);
};
- const selectNewValue = (event, newValue) => {
+ const selectNewValue = (event, option, origin = 'options') => {
+ let reason = 'select-option';
+ let newValue = option;
+
if (multiple) {
- const item = newValue;
newValue = Array.isArray(value) ? [...value] : [];
- const itemIndex = findIndex(newValue, valueItem => getOptionSelected(item, valueItem));
+ const itemIndex = findIndex(newValue, valueItem => getOptionSelected(option, valueItem));
if (itemIndex === -1) {
- newValue.push(item);
- } else {
+ newValue.push(option);
+ } else if (origin !== 'freeSolo') {
newValue.splice(itemIndex, 1);
+ reason = 'remove-option';
}
}
resetInputValue(event, newValue);
- handleValue(event, newValue);
+ handleValue(event, newValue, reason, {
+ option,
+ });
+
if (!disableCloseOnSelect) {
handleClose(event);
}
@@ -525,7 +531,7 @@ export default function useAutocomplete(props) {
onInputChange(event, '', 'clear');
}
- handleValue(event, multiple ? [] : null);
+ handleValue(event, multiple ? [] : null, 'clear');
};
const handleKeyDown = other => event => {
@@ -597,7 +603,7 @@ export default function useAutocomplete(props) {
// Allow people to add new values before they submit the form.
event.preventDefault();
}
- selectNewValue(event, inputValue);
+ selectNewValue(event, inputValue, 'freeSolo');
}
break;
case 'Escape':
@@ -620,7 +626,9 @@ export default function useAutocomplete(props) {
const index = focusedTag === -1 ? value.length - 1 : focusedTag;
const newValue = [...value];
newValue.splice(index, 1);
- handleValue(event, newValue);
+ handleValue(event, newValue, 'remove-option', {
+ option: value[index],
+ });
}
break;
default:
@@ -649,7 +657,7 @@ export default function useAutocomplete(props) {
}
if (autoSelect && selectedIndexRef.current !== -1) {
- handleValue(event, filteredOptions[selectedIndexRef.current]);
+ handleValue(event, filteredOptions[selectedIndexRef.current], 'blur');
} else if (!freeSolo) {
resetInputValue(event, value);
}
@@ -674,7 +682,7 @@ export default function useAutocomplete(props) {
}
if (!disableClearable && !multiple) {
- handleValue(event, null);
+ handleValue(event, null, 'clear');
}
} else {
handleOpen(event);
@@ -710,7 +718,9 @@ export default function useAutocomplete(props) {
const handleTagDelete = index => event => {
const newValue = [...value];
newValue.splice(index, 1);
- handleValue(event, newValue);
+ handleValue(event, newValue, 'remove-option', {
+ option: value[index],
+ });
};
const handleListboxRef = useEventCallback(node => {
@@ -992,6 +1002,7 @@ useAutocomplete.propTypes = {
*
* @param {object} event The event source of the callback
* @param {any} value
+ * @param {string} reason One of "input" (user input) or "reset" (programmatic change).
*/
onChange: PropTypes.func,
/** |
@oliviertassinari Should we add to the details of handleValue(event, newValue, 'remove-option', {
option: value[index],
method: 'backspace'
}); |
@m4theushw I don't know. I think that it's a tricky problem. I would suggest that we do it incrementally: expose the minimum of information possible and based on compelling use cases, broaden it. In this context, the use case seems to be really about gaining information on the individual option interacted with rather than the whole set of selected options. Now, because we frequently have a third "reason" argument, it makes sense to include it now and to add a 4th and final argument, with a rich object structure. What do you think? |
@oliviertassinari No problem, keeping the 4th argument with only the removed option is good by now. If someone asks we can add more information to the details object in the future. |
@oliviertassinari looks good to me |
If you guys don't mind i'll work on that. |
Is there a way to get only the option that was added/removed in the onChange event handler? Controlled auto complete shows this as their onChange handler:
So when i use this for multiple my previous state is [A,B] and newValue is [A,B,C]. I have to diff the previous state with next state to see what was added or removed. When I tried using event.target.value, the first onChange returns
0
then after gives me the correct value that changed.The text was updated successfully, but these errors were encountered: