Skip to content

Commit 1355142

Browse files
committed
[WRFE-70](feat): 태그 커스텀 훅 추가
1 parent f81f535 commit 1355142

File tree

2 files changed

+102
-66
lines changed

2 files changed

+102
-66
lines changed
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import {useState, useEffect} from 'react';
2+
import {
3+
useCreateTag,
4+
useTagQuery,
5+
} from '@/features/product/tag/api/useTagQuery';
6+
import {useDebounce} from '@/shared/hook';
7+
8+
interface TagList {
9+
id: number;
10+
name: string;
11+
}
12+
13+
interface UseTagManagementProps {
14+
tagIds: number[];
15+
onTagChange: (newTags: number[]) => void;
16+
}
17+
18+
interface UseTagManagementReturn {
19+
inputValue: string;
20+
setInputValue: (value: string) => void;
21+
selectedTagNames: string[];
22+
setSelectedTagNames: (tags: string[]) => void;
23+
autocompleteTags: TagList[];
24+
isQueryPending: boolean;
25+
handleAddTag: (tag: string, id: number) => Promise<void>;
26+
}
27+
28+
export const useTagManagement = ({
29+
tagIds,
30+
onTagChange,
31+
}: UseTagManagementProps): UseTagManagementReturn => {
32+
const [inputValue, setInputValue] = useState('');
33+
const [selectedTagNames, setSelectedTagNames] = useState<string[]>([]);
34+
const [autocompleteTags, setAutocompleteTags] = useState<TagList[]>([]);
35+
const [isNew, setIsNew] = useState(true);
36+
37+
const debouncedInputValue = useDebounce(inputValue);
38+
const {mutateAsync: createTag} = useCreateTag();
39+
const {isPending: isQueryPending, data} = useTagQuery({
40+
itemsPerPage: 100,
41+
uuid: '',
42+
prefix: debouncedInputValue.toLocaleLowerCase(),
43+
});
44+
45+
const serverTags = data?.items;
46+
47+
useEffect(() => {
48+
if (!serverTags) return;
49+
50+
setIsNew(true);
51+
52+
const isExistingTag = serverTags.some(tag => tag.name === inputValue);
53+
54+
if (isExistingTag) {
55+
const suggestions = serverTags.filter(tag => tag.name !== inputValue);
56+
setAutocompleteTags(suggestions);
57+
setIsNew(false);
58+
} else {
59+
setAutocompleteTags(serverTags);
60+
}
61+
}, [data, inputValue, serverTags]);
62+
63+
const handleAddTag = async (tag: string, id: number) => {
64+
if (selectedTagNames.length >= 5 || selectedTagNames.includes(tag)) {
65+
setInputValue('');
66+
return;
67+
}
68+
69+
if (isNew && id === 0) {
70+
const newTag = await createTag(tag);
71+
onTagChange([...tagIds, newTag.id]);
72+
} else if (!isNew && id === 0) {
73+
const foundid = serverTags?.find(item => item.name === tag)?.id ?? 0;
74+
onTagChange([...tagIds, foundid]);
75+
} else {
76+
onTagChange([...tagIds, id]);
77+
}
78+
79+
setSelectedTagNames(prev => [...prev, tag]);
80+
setInputValue('');
81+
};
82+
83+
return {
84+
inputValue,
85+
setInputValue,
86+
selectedTagNames,
87+
setSelectedTagNames,
88+
autocompleteTags,
89+
isQueryPending,
90+
handleAddTag,
91+
};
92+
};

apps/front/wraffle-webview/src/widgets/product-list/create/ui/Section/TagSection.tsx

Lines changed: 10 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,24 @@
1-
import {
2-
useCreateTag,
3-
useTagQuery,
4-
} from '../../../../../features/product/tag/api/useTagQuery';
5-
import {useEffect, useState} from 'react';
6-
import {
7-
addTagFromServerData,
8-
addTagFromUserInputIfDuplicate,
9-
createTagAndAddToList,
10-
} from '@/features/product/tag/config/utils';
11-
import {useDebounce} from '@/shared/hook';
1+
import {useTagManagement} from '../../hooks/useTagManagement';
122
import {InputWithSearchIcon} from '@/shared/ui/input/InputWithSearchIcon';
133
import {Tags} from '@/shared/ui/tag/Tags';
144
import {TAG_LIMIT} from '@/shared/util';
155
import {Label} from '@wraffle/ui';
166

17-
interface TagList {
18-
id: number;
19-
name: string;
20-
}
21-
227
interface TagSectionProps {
238
tagIds: number[];
249
onTagChange: (newTags: number[]) => void;
2510
}
2611

2712
export const TagSection = ({tagIds, onTagChange}: TagSectionProps) => {
28-
const [inputValue, setInputValue] = useState(''); // 입력하는 값
29-
const debouncedInputValue = useDebounce(inputValue);
30-
31-
const [autocompleteTags, setAutocompleteTags] = useState<TagList[]>([]); // 서버에서 받아오는 태그 리스트
32-
33-
const [isNew, setIsNew] = useState<boolean>(true); // 새로 생성할지 말지
34-
35-
const [selectedTagNames, setSelectedTagNames] = useState<string[]>([]); // 화면에 보여지는 태그 리스트
36-
37-
const {mutateAsync: createTag} = useCreateTag();
38-
const {isPending: isQueryPending, data} = useTagQuery({
39-
itemsPerPage: 100, //
40-
uuid: '',
41-
prefix: debouncedInputValue.toLocaleLowerCase(),
42-
});
43-
const serverTags = data?.items;
44-
45-
useEffect(() => {
46-
if (!serverTags) return;
47-
48-
setIsNew(true);
49-
50-
const isExistingTag = serverTags.some(tag => tag.name === inputValue);
51-
52-
if (isExistingTag) {
53-
const suggestions = serverTags.filter(tag => tag.name !== inputValue);
54-
setAutocompleteTags(suggestions);
55-
setIsNew(false);
56-
} else {
57-
setAutocompleteTags(serverTags);
58-
}
59-
}, [data, inputValue, serverTags]);
60-
61-
const handleAddTag = async (tag: string, id: number) => {
62-
if (selectedTagNames.length >= 5 || selectedTagNames.includes(tag)) {
63-
setInputValue('');
64-
return;
65-
}
66-
67-
if (isNew && id === 0) {
68-
createTagAndAddToList({tag, createTag, tagIds, onTagChange});
69-
} else if (!isNew && id === 0) {
70-
addTagFromUserInputIfDuplicate({tag, serverTags, tagIds, onTagChange});
71-
} else {
72-
addTagFromServerData({id, tagIds, onTagChange});
73-
}
74-
75-
setSelectedTagNames(prev => [...prev, tag]);
76-
setInputValue('');
77-
};
13+
const {
14+
inputValue,
15+
setInputValue,
16+
selectedTagNames,
17+
setSelectedTagNames,
18+
autocompleteTags,
19+
isQueryPending,
20+
handleAddTag,
21+
} = useTagManagement({tagIds, onTagChange});
7822

7923
return (
8024
<div className='relative'>

0 commit comments

Comments
 (0)