Skip to content

Commit 18a777d

Browse files
committed
perf(select): prevent unnecessary updates with deep value comparison
1 parent 2cb5f1d commit 18a777d

File tree

2 files changed

+35
-4
lines changed

2 files changed

+35
-4
lines changed

core/components/organisms/select/Select.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { Popover, OutsideClick } from '@/index';
77
import SelectTrigger, { SelectTriggerProps } from './SelectTrigger';
88
import SearchInput from './SearchInput';
99
import SelectEmptyTemplate from './SelectEmptyTemplate';
10-
import { focusListItem, mapInitialValue } from './utils';
10+
import { focusListItem, mapInitialValue, isValueEqual } from './utils';
1111
import SelectFooter from './SelectFooter';
1212
import { BaseProps, extractBaseProps } from '@/utils/types';
1313
import { PopoverProps } from '@/index.type';
@@ -168,6 +168,7 @@ export const Select = React.forwardRef<SelectMethods, SelectProps>((props, ref)
168168

169169
const triggerRef = React.createRef<HTMLButtonElement>();
170170
const listRef = React.useRef<HTMLDivElement | null>(null);
171+
const prevValueRef = React.useRef<OptionType | OptionType[] | undefined>(value);
171172

172173
const [withSearch, setWithSearch] = React.useState(false);
173174

@@ -237,9 +238,13 @@ export const Select = React.forwardRef<SelectMethods, SelectProps>((props, ref)
237238
}, [highlightLastItem]);
238239

239240
React.useEffect(() => {
240-
if (value) {
241-
setSelectValue(value);
242-
setIsOptionSelected(Array.isArray(value) ? value.length > 0 : value && 'value' in value);
241+
// Only update if the value has actually changed (deep comparison)
242+
if (!isValueEqual(prevValueRef.current, value)) {
243+
prevValueRef.current = value;
244+
if (value) {
245+
setSelectValue(value);
246+
setIsOptionSelected(Array.isArray(value) ? value.length > 0 : value && 'value' in value);
247+
}
243248
}
244249
}, [value]);
245250

core/components/organisms/select/utils.tsx

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,32 @@ export const mapInitialValue = (multiSelect: boolean, selectedValue: OptionType
99
}
1010
};
1111

12+
export const isValueEqual = (
13+
value1: OptionType | OptionType[] | undefined,
14+
value2: OptionType | OptionType[] | undefined
15+
): boolean => {
16+
// Both undefined or null
17+
if (!value1 && !value2) return true;
18+
if (!value1 || !value2) return false;
19+
20+
// Both are arrays
21+
if (Array.isArray(value1) && Array.isArray(value2)) {
22+
if (value1.length !== value2.length) return false;
23+
return value1.every((item, index) => {
24+
const item2 = value2[index];
25+
return item.label === item2.label && item.value === item2.value;
26+
});
27+
}
28+
29+
// Both are single objects
30+
if (!Array.isArray(value1) && !Array.isArray(value2)) {
31+
return value1.label === value2.label && value1.value === value2.value;
32+
}
33+
34+
// One is array, other is not
35+
return false;
36+
};
37+
1238
export const elementExist = (targetObject: OptionType, mainList: OptionType | OptionType[] | undefined) => {
1339
if (!targetObject || !targetObject.label) {
1440
return -1;

0 commit comments

Comments
 (0)