Skip to content

Commit

Permalink
feat: (#619) 통계 페이지 리액트 쿼리로 수정 및 서스펜스, 에러바운더리 적용
Browse files Browse the repository at this point in the history
  • Loading branch information
chsua committed Sep 15, 2023
1 parent 891cb8f commit 20ca82a
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 86 deletions.
1 change: 1 addition & 0 deletions frontend/src/constants/queryKey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ export const QUERY_KEY = {
USER_INFO: 'user_info',
PASSION_RANKING: 'passion_ranking',
POPULAR_RANKING: 'popular_ranking',
VOTE_STATISTICS: 'vote_statistics',
};
21 changes: 21 additions & 0 deletions frontend/src/hooks/query/useVoteStatistics.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { useQuery } from '@tanstack/react-query';

import { getOptionStatistics, getPostStatistics } from '@api/voteResult';

import { VoteResult } from '@components/VoteStatistics/type';

import { QUERY_KEY } from '@constants/queryKey';

export const useVoteStatistics = (postId: number, optionId?: number) => {
const { data } = useQuery<VoteResult>(
optionId ? [QUERY_KEY.VOTE_STATISTICS, postId, optionId] : [QUERY_KEY.VOTE_STATISTICS, postId],
() => (optionId ? getOptionStatistics({ postId, optionId }) : getPostStatistics(postId)),
{
cacheTime: 60 * 60 * 1000,
staleTime: 60 * 60 * 1000,
suspense: true,
}
);

return { data };
};
45 changes: 16 additions & 29 deletions frontend/src/pages/VoteStatisticsPage/OptionStatistics/index.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
import { useState } from 'react';
import { Suspense, useState } from 'react';

import { WrittenVoteOptionType } from '@type/post';
import { Size } from '@type/style';

import { useFetch } from '@hooks/useFetch';
import { useToast } from '@hooks/useToast';

import { getOptionStatistics } from '@api/voteResult';
import ErrorBoundary from '@pages/ErrorBoundary';

import LoadingSpinner from '@components/common/LoadingSpinner';
import Toast from '@components/common/Toast';
import WrittenVoteOption from '@components/optionList/WrittenVoteOptionList/WrittenVoteOption';
import VoteStatistics from '@components/VoteStatistics';

import StatisticsWrapper from '../StatisticsWrapper';

import * as S from './style';

Expand All @@ -29,17 +26,8 @@ export default function OptionStatistics({
size,
}: OptionStatisticsProps) {
const [isStatisticsOpen, setIsStatisticsOpen] = useState(false);
const { isToastOpen, openToast, toastMessage } = useToast();

const {
data: voteResult,
errorMessage,
isLoading,
} = useFetch(() => getOptionStatistics({ postId, optionId: voteOption.id }));

const toggleOptionStatistics = () => {
if (!voteResult) return openToast('투표 통계 불러오기를 실패했습니다.');

setIsStatisticsOpen(!isStatisticsOpen);
};

Expand All @@ -60,26 +48,25 @@ export default function OptionStatistics({
투표 선택지를 클릭하여 투표 통계를 열어 확인할 수 있습니다.
</S.ScreenReaderDirection>
)}
{isStatisticsOpen && voteResult && (
{isStatisticsOpen && (
<>
<S.ScreenReaderDirection>
투표 선택지를 클릭하여 투표 통계를 닫을 수 있습니다.
</S.ScreenReaderDirection>
<VoteStatistics voteResultResponse={voteResult} size={size} />
<ErrorBoundary>
<Suspense
fallback={
<S.LoadingWrapper>
<LoadingSpinner size="sm" />
</S.LoadingWrapper>
}
>
<StatisticsWrapper postId={postId} optionId={voteOption.id} size={size} />
</Suspense>
</ErrorBoundary>
</>
)}
{isStatisticsOpen && isLoading && (
<S.LoadingWrapper>
<LoadingSpinner size="sm" />
</S.LoadingWrapper>
)}
{isStatisticsOpen && errorMessage}
</S.StatisticsContainer>
{isToastOpen && (
<Toast size="md" position="bottom">
{toastMessage}
</Toast>
)}
</S.Container>
);
}
35 changes: 35 additions & 0 deletions frontend/src/pages/VoteStatisticsPage/OptionWrapper/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { useContext } from 'react';
import { Navigate } from 'react-router-dom';

import { AuthContext } from '@hooks/context/auth';
import { usePostDetail } from '@hooks/query/post/usePostDetail';

import { PATH } from '@constants/path';

import { checkWriter } from '@utils/post/checkWriter';

import OptionStatistics from '../OptionStatistics';

export default function OptionWrapper({ postId }: { postId: number }) {
const { isLoggedIn } = useContext(AuthContext).loggedInfo;
const { data: postDetail } = usePostDetail(isLoggedIn, postId);

if (!isLoggedIn && postDetail && !checkWriter(postDetail.writer.id))
return <Navigate to={PATH.HOME} />;

return (
postDetail &&
postDetail.voteInfo.options.map(option => {
const { postId, voteInfo } = postDetail;
return (
<OptionStatistics
key={option.id}
postId={postId}
isSelectedOption={voteInfo.selectedOptionId === option.id}
voteOption={option}
size="sm"
/>
);
})
);
}
19 changes: 19 additions & 0 deletions frontend/src/pages/VoteStatisticsPage/StatisticsWrapper/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Size } from '@type/style';

import { useVoteStatistics } from '@hooks/query/useVoteStatistics';

import VoteStatistics from '@components/VoteStatistics';

export default function StatisticsWrapper({
size,
postId,
optionId,
}: {
size: Size;
postId: number;
optionId?: number;
}) {
const { data: voteResult } = useVoteStatistics(postId, optionId);

return voteResult && <VoteStatistics voteResult={voteResult} size={size} />;
}
77 changes: 20 additions & 57 deletions frontend/src/pages/VoteStatisticsPage/index.tsx
Original file line number Diff line number Diff line change
@@ -1,44 +1,22 @@
import { Navigate, useNavigate, useParams } from 'react-router-dom';
import { Suspense } from 'react';
import { useNavigate, useParams } from 'react-router-dom';

import { useFetch } from '@hooks/useFetch';
import ErrorBoundary from '@pages/ErrorBoundary';

import { getPost } from '@api/post';
import { getPostStatistics } from '@api/voteResult';

import ErrorMessage from '@components/common/ErrorMessage';
import IconButton from '@components/common/IconButton';
import Layout from '@components/common/Layout';
import LoadingSpinner from '@components/common/LoadingSpinner';
import NarrowTemplateHeader from '@components/common/NarrowTemplateHeader';
import Skeleton from '@components/common/Skeleton';
import VoteStatistics from '@components/VoteStatistics';

import { PATH } from '@constants/path';

import { checkWriter } from '@utils/post/checkWriter';

import OptionStatistics from './OptionStatistics';
import OptionWrapper from './OptionWrapper';
import StatisticsWrapper from './StatisticsWrapper';
import * as S from './style';

export default function VoteStatisticsPage() {
const params = useParams() as { postId: string };
const postId = Number(params.postId);

const navigate = useNavigate();

const {
data: postDetail,
errorMessage: postError,
isLoading: isPostLoading,
} = useFetch(() => getPost(postId));

const {
data: voteResultResponse,
errorMessage: voteResultError,
isLoading: isVoteResultLoading,
} = useFetch(() => getPostStatistics(postId));

if (postDetail && !checkWriter(postDetail.writer.id)) return <Navigate to={PATH.HOME} />;

return (
<Layout isSidebarVisible={true}>
<S.HeaderWrapper>
Expand All @@ -48,37 +26,22 @@ export default function VoteStatisticsPage() {
</S.HeaderWrapper>
<S.Container>
<S.PageHeader>투표 통계</S.PageHeader>
{postError && <ErrorMessage />}
{isPostLoading && (
<S.LoadingWrapper>
<Skeleton isLarge={true} />
</S.LoadingWrapper>
)}
{postDetail && (
<S.OptionContainer>
{voteResultError && <ErrorMessage />}
{isVoteResultLoading && (
<S.OptionContainer>
<Suspense
fallback={
<S.LoadingWrapper>
<Skeleton isLarge={true} />
<LoadingSpinner size="sm" />
</S.LoadingWrapper>
)}
{voteResultResponse && (
<VoteStatistics voteResultResponse={voteResultResponse} size="md" />
)}
{postDetail.voteInfo.options.map(option => {
const { postId, voteInfo } = postDetail;
return (
<OptionStatistics
key={option.id}
postId={postId}
isSelectedOption={voteInfo.selectedOptionId === option.id}
voteOption={option}
size="sm"
/>
);
})}
</S.OptionContainer>
)}
}
>
<ErrorBoundary>
<StatisticsWrapper postId={postId} size="md" />
</ErrorBoundary>
</Suspense>
<ErrorBoundary>
<OptionWrapper postId={postId} />
</ErrorBoundary>
</S.OptionContainer>
</S.Container>
</Layout>
);
Expand Down

0 comments on commit 20ca82a

Please sign in to comment.