Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 52 additions & 19 deletions packages/ui/modules/hooks/useListValuesAutocomplete.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ const useListValuesAutocomplete = ({
asyncFetch, useLoadMore, useAsyncSearch, forceAsyncSearch,
asyncListValues: selectedAsyncListValues,
listValues: staticListValues, allowCustomValues,
value: selectedValue, setValue, placeholder,
config
value: selectedValue, setValue, placeholder,
config,field
}, {
debounceTimeout,
multiple,
Expand All @@ -41,9 +41,21 @@ const useListValuesAutocomplete = ({

// compute
const nSelectedAsyncListValues = listValuesToArray(selectedAsyncListValues);
const listValues = asyncFetch
? (selectedAsyncListValues ? mergeListValues(asyncListValues, nSelectedAsyncListValues, true) : asyncListValues)
: staticListValues;
// const listValues = asyncFetch
// ? (selectedAsyncListValues ? mergeListValues(asyncListValues, nSelectedAsyncListValues, true) : asyncListValues)
// : staticListValues;
const listValues = React.useMemo(() => {
Copy link

Copilot AI May 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure both async and static list values are consistently converted to arrays. Consider clarifying in an inline comment why an empty array is used as a fallback in the memoized computation.

Suggested change
const listValues = React.useMemo(() => {
const listValues = React.useMemo(() => {
// Ensure consistent array handling:
// - For async values, fallback to an empty array if asyncListValues is undefined.
// - For static values, convert to an array using listValuesToArray.
// This prevents runtime errors and ensures compatibility with downstream operations.

Copilot uses AI. Check for mistakes.
return asyncFetch
? (selectedAsyncListValues
? mergeListValues(asyncListValues || [], nSelectedAsyncListValues, true)
: asyncListValues || [])
: listValuesToArray(staticListValues);
}, [asyncFetch, selectedAsyncListValues, asyncListValues, staticListValues, nSelectedAsyncListValues]);

React.useEffect(() => {
setAsyncListValues(undefined);
}, [field]);
Copy link

Copilot AI May 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Consider adding a brief comment explaining why async list values are reset when the field changes to improve code clarity and maintainability.

Copilot uses AI. Check for mistakes.

let listValuesToDisplay = asyncFetch
? asyncListValues
: staticListValues;
Expand Down Expand Up @@ -95,7 +107,7 @@ const useListValuesAutocomplete = ({
const { values, hasMore, meta: newMeta } = res?.values
? res
: { values: res } // fallback, if response contains just array, not object
;
;
const nValues = listValuesToArray(values);
let assumeHasMore;
let newValues;
Expand Down Expand Up @@ -123,21 +135,42 @@ const useListValuesAutocomplete = ({
return newValues;
};

// const loadListValues = async (filter = null, isLoadMore = false) => {
// setLoadingCnt(x => (x + 1));
// setIsLoadingMore(isLoadMore);
// const list = await fetchListValues(filter, isLoadMore);
// if (!componentIsMounted.current) {
// return;
// }
// if (list != null) {
// // tip: null can be used for reject (eg, if user don't want to filter by input)
// setAsyncListValues(list);
// }
// setLoadingCnt(x => (x - 1));
// setIsLoadingMore(false);
// };
const loadListValues = async (filter = null, isLoadMore = false) => {
if (!isLoadMore) {
setAsyncListValues(undefined);
}

setLoadingCnt(x => (x + 1));
setIsLoadingMore(isLoadMore);
const list = await fetchListValues(filter, isLoadMore);
if (!componentIsMounted.current) {
return;
}
if (list != null) {
// tip: null can be used for reject (eg, if user don't want to filter by input)
setAsyncListValues(list);
}
setLoadingCnt(x => (x - 1));
setIsLoadingMore(false);
};
Copy link

Copilot AI May 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

loadListValues is defined inline and may change on every render, causing the debounced function to be recreated frequently. Consider memoizing loadListValues itself to ensure a stable reference and avoid unnecessary re-instantiation of the debounced function.

Suggested change
};
}, [fetchListValues, setAsyncListValues, setLoadingCnt, setIsLoadingMore, componentIsMounted]);

Copilot uses AI. Check for mistakes.
const loadListValuesDebounced = React.useCallback(debounce(loadListValues, debounceTimeout), []);
// const loadListValuesDebounced = React.useCallback(debounce(loadListValues, debounceTimeout), []);
const loadListValuesDebounced = React.useMemo(
() => debounce(loadListValues, debounceTimeout),
[loadListValues, debounceTimeout]
);

React.useEffect(() => {
componentIsMounted.current++;
Expand Down Expand Up @@ -215,10 +248,10 @@ const useListValuesAutocomplete = ({
if (shouldIgnore) {
return;
}
const isAddingCustomOptionFromSearch
const isAddingCustomOptionFromSearch
= multiple
&& val.length && val.length > (selectedValue || []).length
&& val[val.length-1] == inputValue
&& val[val.length - 1] == inputValue
&& !getListValue(inputValue, asyncListValues);

if (specialValue == "LOAD_MORE") {
Expand All @@ -231,7 +264,7 @@ const useListValuesAutocomplete = ({
if (multiple) {
const [newSelectedValues, newSelectedListValues] = optionsToListValues(val, listValues, allowCustomValues);
setValue(newSelectedValues, asyncFetch ? newSelectedListValues : undefined);

if (isAddingCustomOptionFromSearch) {
await sleep(0);
await onInputChange(null, "", "my-reset");
Expand All @@ -253,13 +286,13 @@ const useListValuesAutocomplete = ({
// - (multiple v4) select option while searching - e = null, newInputValue = '' # unwanted

const shouldIgnore = uif === "mui" && eventType === "reset"
// && (
// e != null
// // for MUI 4 if search "A" and select any option -> should NOT reset search
// // for MUI 5 if search "A" and close -> let's hold search but hide, as it's done in antd
// || e === null && inputValue && multiple
// )
;
// && (
// e != null
// // for MUI 4 if search "A" and select any option -> should NOT reset search
// // for MUI 5 if search "A" and close -> let's hold search but hide, as it's done in antd
// || e === null && inputValue && multiple
// )
;
const val = newInputValue;
if (val === loadMoreTitle || val === loadingMoreTitle || shouldIgnore) {
return;
Expand Down Expand Up @@ -349,7 +382,7 @@ const useListValuesAutocomplete = ({
onClose,
onDropdownVisibleChange,
onChange,

inputValue,
onInputChange,
canShowLoadMore,
Expand Down