useEquippedItems.ts 1.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051
  1. 'use client';
  2. // D3 장착 지위 아이템 배치 조회 훅 — 여러 작성자 ID 를 모아 한 번에 GET /api/members/equipped-items?ids=.
  3. // 댓글 목록/프로필에서 작성자별 배지·닉네임 이펙트 렌더용. 익명 허용.
  4. import { useEffect, useState } from 'react';
  5. import { fetchApi } from '@/lib/utils/client';
  6. import type { EquippedItem, EquippedItemsResponse } from '@/types/reward';
  7. type EquippedMap = Record<number, EquippedItem[]>;
  8. export default function useEquippedItems(memberIDs: (number|null|undefined)[]): EquippedMap
  9. {
  10. const [map, setMap] = useState<EquippedMap>({});
  11. // 유효 ID 만 정렬·중복 제거해 안정적인 key 생성 (불필요한 재조회 방지)
  12. const ids = Array.from(new Set(memberIDs.filter((id): id is number => typeof id === 'number' && id > 0))).sort((a, b) => a - b);
  13. const key = ids.join(',');
  14. useEffect(() => {
  15. if (ids.length === 0) {
  16. setMap({});
  17. return;
  18. }
  19. let cancelled = false;
  20. (async () => {
  21. const res = await fetchApi<EquippedItemsResponse>(`/api/members/equipped-items?ids=${key}`, { silent: true });
  22. if (cancelled) {
  23. return;
  24. }
  25. if (res.success && res.data) {
  26. const next: EquippedMap = {};
  27. for (const m of res.data.members) {
  28. next[m.memberID] = m.items;
  29. }
  30. setMap(next);
  31. }
  32. else {
  33. setMap({});
  34. }
  35. })();
  36. return () => {
  37. cancelled = true;
  38. };
  39. }, [key]); // eslint-disable-line react-hooks/exhaustive-deps
  40. return map;
  41. }