fix: Improve option filtering in V2Select component

- Updated the option filtering logic to handle null and undefined values, preventing potential crashes when cmdk encounters these values.
- Introduced a safeOptions variable to ensure that only valid options are processed in the dropdown and command list.
- Enhanced the setOptions function to sanitize fetched options, ensuring that only valid values are set, improving overall stability and user experience.
This commit is contained in:
kjs 2026-02-27 12:06:49 +09:00
parent 0f52c3adc2
commit c1f7f27005
1 changed files with 16 additions and 6 deletions

View File

@ -80,7 +80,7 @@ const DropdownSelect = forwardRef<HTMLButtonElement, {
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
{options {options
.filter((option) => option.value !== "") .filter((option) => option.value != null && option.value !== "")
.map((option) => ( .map((option) => (
<SelectItem key={option.value} value={option.value}> <SelectItem key={option.value} value={option.value}>
{option.label} {option.label}
@ -112,6 +112,12 @@ const DropdownSelect = forwardRef<HTMLButtonElement, {
} }
// 검색 가능 또는 다중 선택 → Combobox 사용 // 검색 가능 또는 다중 선택 → Combobox 사용
// null/undefined value를 가진 옵션 필터링 (cmdk가 value={null}일 때 크래시 발생)
const safeOptions = useMemo(() =>
options.filter((o) => o.value != null && o.value !== ""),
[options]
);
const selectedValues = useMemo(() => { const selectedValues = useMemo(() => {
if (!value) return []; if (!value) return [];
return Array.isArray(value) ? value : [value]; return Array.isArray(value) ? value : [value];
@ -119,9 +125,9 @@ const DropdownSelect = forwardRef<HTMLButtonElement, {
const selectedLabels = useMemo(() => { const selectedLabels = useMemo(() => {
return selectedValues return selectedValues
.map((v) => options.find((o) => o.value === v)?.label) .map((v) => safeOptions.find((o) => o.value === v)?.label)
.filter(Boolean) as string[]; .filter(Boolean) as string[];
}, [selectedValues, options]); }, [selectedValues, safeOptions]);
const handleSelect = useCallback((selectedValue: string) => { const handleSelect = useCallback((selectedValue: string) => {
if (multiple) { if (multiple) {
@ -191,7 +197,7 @@ const DropdownSelect = forwardRef<HTMLButtonElement, {
<Command <Command
filter={(itemValue, search) => { filter={(itemValue, search) => {
if (!search) return 1; if (!search) return 1;
const option = options.find((o) => o.value === itemValue); const option = safeOptions.find((o) => o.value === itemValue);
const label = (option?.label || option?.value || "").toLowerCase(); const label = (option?.label || option?.value || "").toLowerCase();
if (label.includes(search.toLowerCase())) return 1; if (label.includes(search.toLowerCase())) return 1;
return 0; return 0;
@ -201,7 +207,7 @@ const DropdownSelect = forwardRef<HTMLButtonElement, {
<CommandList> <CommandList>
<CommandEmpty> .</CommandEmpty> <CommandEmpty> .</CommandEmpty>
<CommandGroup> <CommandGroup>
{options.map((option) => { {safeOptions.map((option) => {
const displayLabel = option.label || option.value || "(빈 값)"; const displayLabel = option.label || option.value || "(빈 값)";
return ( return (
<CommandItem <CommandItem
@ -869,7 +875,11 @@ export const V2Select = forwardRef<HTMLDivElement, V2SelectProps>(
} }
} }
setOptions(fetchedOptions); // null/undefined value 필터링 (cmdk 크래시 방지)
const sanitized = fetchedOptions.filter(
(o) => o.value != null && String(o.value) !== ""
).map((o) => ({ ...o, value: String(o.value), label: o.label || String(o.value) }));
setOptions(sanitized);
setOptionsLoaded(true); setOptionsLoaded(true);
} catch (error) { } catch (error) {
console.error("옵션 로딩 실패:", error); console.error("옵션 로딩 실패:", error);