Skip to content

Commit

Permalink
♻️ refactor(#167): 내 대시보드 서버 사이드 렌더링 수정
Browse files Browse the repository at this point in the history
  • Loading branch information
wjsdncl committed Jul 4, 2024
1 parent 7be8f0d commit 8b953d1
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 60 deletions.
54 changes: 28 additions & 26 deletions src/containers/mydashboard/DashboardList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ export default function DashboardList({ initialDashboard }: DashboardListProps)
const [currentChunk, setCurrentChunk] = useState<number>(1);
const { openNewDashboardModal } = useModal();
const [dashboardData, setDashboards] = useState<DashboardsResponse>(initialDashboard);
const [isInitial, setIsInitial] = useState<boolean>(true);
const [isInitial, setIsInitial] = useState<boolean>(!!initialDashboard);

// 대시보드 데이터를 가져오는 커스텀 훅 사용
const {
data: dashboardResponse,
error,
Expand All @@ -26,7 +27,19 @@ export default function DashboardList({ initialDashboard }: DashboardListProps)
getDashboardsList('pagination', currentChunk, 5),
);

const totalPage = initialDashboard.totalCount ? Math.max(1, Math.ceil(initialDashboard.totalCount / 5)) : 1;
// 총 페이지 수 계산
const totalPage = initialDashboard?.totalCount ? Math.max(1, Math.ceil(initialDashboard.totalCount / 5)) : 1;

// 대시보드 데이터가 변경될 때 상태 업데이트
useEffect(() => {
if (dashboardResponse) {
if (!isInitial) {
setDashboards(dashboardResponse);
} else {
setIsInitial(false);
}
}
}, [dashboardResponse, isInitial]);

if (error) {
return (
Expand All @@ -37,53 +50,44 @@ export default function DashboardList({ initialDashboard }: DashboardListProps)
);
}

// 다음 페이지로 이동하는 함수
const handleNext = () => {
if (currentChunk < totalPage) {
setCurrentChunk((prev) => prev + 1);
}
};

// 이전 페이지로 이동하는 함수
const handlePrev = () => {
if (currentChunk > 1) {
setCurrentChunk((prev) => prev - 1);
}
};

useEffect(() => {
if (dashboardResponse) {
if (!isInitial) {
setDashboards(dashboardResponse);
} else {
setIsInitial(false);
}
}
}, [dashboardResponse]);

if (!dashboardData) return;
if (!dashboardData) return null;

return (
<section className='flex-col justify-between'>
<ul className='grid max-w-[350px] grid-rows-1 gap-3 font-semibold text-black-33 md:min-h-[216px] md:max-w-full md:grid-cols-2 md:grid-rows-3 lg:min-h-[140px] lg:max-w-screen-lg lg:grid-cols-3'>
{/* 새로운 대시보드 생성 버튼 */}
<li className='h-12 w-full rounded-lg border border-gray-d9 bg-white md:h-16'>
<button className='btn-violet-light size-full gap-4' type='button' onClick={() => openNewDashboardModal()}>
새로운 대시보드
<Image src={'/icons/plus-filled.svg'} alt='plus' width={22} height={22} />
</button>
</li>
{isLoading && !isInitial ? (
<>
{[...Array(5)].map((_, i) => (
{/* 대시보드 목록 표시 */}
{isLoading && !isInitial
? [...Array(5)].map((_, i) => (
<li key={i} className='h-12 w-full animate-pulse rounded-lg border border-gray-d9 bg-gray-fa md:h-16' />
))}
</>
) : (
<>
{dashboardData.dashboards.map((dashboard) => (
))
: dashboardData.dashboards.map((dashboard) => (
<li className='h-12 w-full rounded-lg border border-gray-d9 bg-white md:h-16' key={dashboard.id}>
<Link href={`/dashboard/${dashboard.id}`} className={'btn-violet-light size-full rounded-md px-5'}>
<Link href={`/dashboard/${dashboard.id}`} className='btn-violet-light size-full rounded-md px-5'>
<div className='flex size-full items-center'>
<div className='rounded-full p-1' style={{ backgroundColor: dashboard.color }} />
<div className='mx-4 h-[28px] grow overflow-hidden text-ellipsis text-lg font-medium'>
<p className={`size-full`}>{dashboard.title}</p>
<p className='size-full'>{dashboard.title}</p>
</div>
{dashboard.createdByMe && (
<Image src={'/icons/crown.svg'} className='mr-3' alt='my' width={20} height={16} />
Expand All @@ -93,9 +97,7 @@ export default function DashboardList({ initialDashboard }: DashboardListProps)
</Link>
</li>
))}
</>
)}

{/* 페이지네이션 */}
<div className='md:col-span-2 lg:col-span-3 lg:row-start-3'>
<Pagination
currentChunk={currentChunk}
Expand Down
72 changes: 39 additions & 33 deletions src/containers/mydashboard/InvitedDashboardList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,76 +16,75 @@ interface InvitedDashboardListProps {
initialInvitedDashboard: InvitationsResponse;
}

export default function InvitedDashboardList({ initialInvitedDashboard }: InvitedDashboardListProps) {
const [invitations, setInvitations] = useState<Invitation[]>(initialInvitedDashboard.invitations);
const [isInitial, setIsInitial] = useState<boolean>(true);
const InvitedDashboardList = ({ initialInvitedDashboard }: InvitedDashboardListProps) => {
const [invitations, setInvitations] = useState<Invitation[]>(initialInvitedDashboard?.invitations || []);
const [isInitial, setIsInitial] = useState<boolean>(!initialInvitedDashboard);
const [isFetchingNextPage, setIsFetchingNextPage] = useState(false);
const [isSearching, setIsSearching] = useState(false);
const observerRef = useRef<HTMLDivElement | null>(null);
const [cursorId, setCursorId] = useState<number>(0);

const queryClient = useQueryClient();

const { data, error, isLoading } = useFetchData<InvitationsResponse>(['invitations'], () => getInvitationsList());
// 데이터를 가져오는 커스텀 훅
const { data, error, isLoading } = useFetchData<InvitationsResponse>(['invitations'], getInvitationsList);

// 데이터가 변경될 때 초대 상태 업데이트
useEffect(() => {
if (data) {
if (!isInitial) {
setInvitations(data.invitations);
setCursorId(data.cursorId ? data.cursorId : 0);
setCursorId(data.cursorId ?? 0);
} else {
setIsInitial(false);
}
}
}, [data]);
}, [data, isInitial]);

const handleMoreInvitations = async (currentCursorId: number) => {
// 더 많은 초대 데이터를 가져오는 함수
const handleMoreInvitations = useCallback(async (currentCursorId: number) => {
if (currentCursorId !== 0) {
setIsFetchingNextPage(true);
try {
setIsFetchingNextPage(true);
const { data: nextData } = await getInvitationsList(10, currentCursorId);

if (nextData.invitations.length > 0) {
setInvitations((prevInvitations) => [...prevInvitations, ...nextData.invitations]);
}
setCursorId(nextData.cursorId || 0);
setCursorId(nextData.cursorId ?? 0);
} catch (err) {
console.error('데이터를 가져오는 중 오류가 발생했습니다:', err);
} finally {
setIsFetchingNextPage(false);
}
}
};
}, []);

// IntersectionObserver 콜백 함수
const handleObserver = useCallback(
async (entries: IntersectionObserverEntry[]) => {
(entries: IntersectionObserverEntry[]) => {
const target = entries[0];

if (target.isIntersecting && !isFetchingNextPage && cursorId && !isSearching) {
handleMoreInvitations(cursorId);
}
},
[cursorId, isFetchingNextPage, isSearching],
[cursorId, isFetchingNextPage, isSearching, handleMoreInvitations],
);

// IntersectionObserver 설정
useEffect(() => {
const observer = new IntersectionObserver(handleObserver, {
threshold: 0.8,
});

const observer = new IntersectionObserver(handleObserver, { threshold: 0.8 });
const currentObserverRef = observerRef.current;

if (currentObserverRef) {
observer.observe(currentObserverRef);
}

return () => {
if (currentObserverRef) {
observer.unobserve(currentObserverRef);
}
};
}, [observerRef, handleObserver]);

// 초대 수락/거절 처리 함수
const handleAcceptInvitation = async (invitationId: number, inviteAccepted: boolean) => {
try {
await putAcceptInvitation(invitationId, inviteAccepted);
Expand All @@ -97,17 +96,20 @@ export default function InvitedDashboardList({ initialInvitedDashboard }: Invite
}
};

const handleChangeSearch = debounce(async (e: React.ChangeEvent<HTMLInputElement>) => {
const searchValue = e.target.value;
setIsSearching(!!searchValue);

try {
const { data: searchData } = await getInvitationsList(10, 0, searchValue);
setInvitations(searchData.invitations);
} catch (err) {
console.error('데이터를 가져오는 중 오류가 발생했습니다:', err);
}
}, 300);
// 검색어 변경 처리 함수
const handleChangeSearch = useCallback(
debounce(async (e: React.ChangeEvent<HTMLInputElement>) => {
const searchValue = e.target.value;
setIsSearching(!!searchValue);
try {
const { data: searchData } = await getInvitationsList(10, 0, searchValue);
setInvitations(searchData.invitations);
} catch (err) {
console.error('데이터를 가져오는 중 오류가 발생했습니다:', err);
}
}, 300),
[],
);

if (error) {
return (
Expand All @@ -121,10 +123,12 @@ export default function InvitedDashboardList({ initialInvitedDashboard }: Invite
);
}

if (!invitations) return null;

return (
<section className='h-[400px] max-w-[350px] rounded-lg border-0 bg-white md:max-h-[740px] md:min-h-[530px] md:max-w-full lg:max-w-screen-lg'>
<p className='px-7 pb-5 pt-8 text-base font-bold text-black-33'>초대받은 대시보드</p>
{isLoading && !isInitial ? (
{isLoading && !isInitial && !initialInvitedDashboard ? (
<Skeleton />
) : (
<>
Expand All @@ -149,4 +153,6 @@ export default function InvitedDashboardList({ initialInvitedDashboard }: Invite
)}
</section>
);
}
};

export default InvitedDashboardList;
2 changes: 1 addition & 1 deletion src/hooks/useSignIn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export const useSignIn = () => {
onSuccess: (data) => {
dispatch(setUser(data));
dispatch(isLoading(false));
setCookie('token', data.accessToken, { httpOnly: true, sameSite: 'strict' });
setCookie('token', data.accessToken);
},
onMutate: async () => {
dispatch(isLoading(true));
Expand Down

0 comments on commit 8b953d1

Please sign in to comment.