Skip to content

Commit

Permalink
feat: 전역 suspense 개선 (#427)
Browse files Browse the repository at this point in the history
* feat: 페이지간 이동 로딩바 컴포넌트 구현

- 페이지 이동 후에도 서서히 사라지는 모션을 보여주기 위해 suspense fallback과 분리함

* feat: 마지막 페이지와 함께 로딩 바를 제어하는 suspense fallback 컴포넌트 구현

- 첫 페이지는 기존의 로딩 스피너 활용

* feat: 마지막 페이지를 기록하는 Wrapper 컴포넌트 추가

* feat: Context 대신 Recoil 사용

* chore: MSW delay 3초로 설정

* refactor: 모든 페이지에 PageLogger 적용

* design: backdrop 제거

* chore: PageLogger 적용

* fix: Page effect에 로딩바 숨기기 추가

* style: lastPageValue 네이밍 변경

* design: 로딩바 속도 2배 증가

* chore: MSW 지연 500으로 변경

* design: 왼쪽 border 제거

* design: 프로그래스 바 속도 증가

* refactor: style 파일 분리

* refactor: isShow 대신 isShowPageLoading 재활용

* fix: 로딩바 깜빡거리는 현상 제거

- 이전과는 달리 보여지는 상태를 같은 Effect 안에서 관리

* chore: MSW delay 200으로 변경

* chore: 초기 로딩 속도 증가

* fix: 애니메이션 겹치는 현상 해결

- finish 애니메이션이 재생중일 때 새로 렌더링 되면 setTimeout 및 상태 초기화

* design: 두께 3px로 변경

* chore: useEffect 의존성 배열에 사용되는 값 추가

* chore: || 대신 ?? 사용

* refactor: 페이지에 Main 태그 적용 및 hooks 폴더 이동

* chore: 폴더 이동 import 적용
  • Loading branch information
bassyu authored Oct 13, 2023
1 parent 9d10fe1 commit 2d47cb9
Show file tree
Hide file tree
Showing 32 changed files with 213 additions and 55 deletions.
2 changes: 2 additions & 0 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { RouterProvider } from 'react-router-dom';
import { RecoilRoot } from 'recoil';
import { ThemeProvider } from 'styled-components';
import Confirm from 'components/@common/Confirm';
import PageLoadingBar from 'components/@common/PageLoadingBar';
import SvgSpriteMap from 'components/@common/SvgIcons/SvgSpriteMap';
import ToastList from 'components/@common/Toast/ToastList';
import { GlobalStyle } from 'style/Global.style';
Expand All @@ -22,6 +23,7 @@ const App = () => {
<RouterProvider router={router} />
<Confirm />
<ToastList />
<PageLoadingBar />
</RecoilRoot>
</QueryClientProvider>
</ThemeProvider>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/@common/Calendar/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { useState } from 'react';
import SvgIcons from 'components/@common/SvgIcons/SvgFill';
import { AlertSpan, Button, CalendarBox, DaysBox, HeaderBox, Wrapper } from './Calendar.style';
import useCalendar from 'hooks/@common/useCalendar';
import { convertDateKorYear, getDateToString, getDayInfo, getDaysBetween } from 'utils/date';
import { DateValidate } from 'utils/validate';
import { DAYS_OF_THE_WEEK } from 'constants/index';
import theme from 'style/theme.style';
import DaySmallBox from './DaySmallBox';
import useCalendar from './hooks/useCalendar';

interface CalendarProps {
selectedDate: Date | null;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { keyframes, styled } from 'styled-components';

export const ProgressBar = styled.div`
position: fixed;
z-index: ${(props) => props.theme.zIndex.tooltip};
top: 0;
left: 0;
width: 100%;
height: 3px;
background-color: ${(props) => props.theme.color.primary};
border-radius: 0 4px 4px 0;
`;

export const progressing = keyframes`
0% { transform: translateX(-100%); }
50% { transform: translateX(-20%); }
100% { transform: translateX(0); }
`;

export const Progressing = styled(ProgressBar)`
animation: ${progressing} 6s ease-out;
`;

export const fillOut = keyframes`
0% {
opacity: 1;
transform: translateX(-100%);
}
50% {
opacity: 1;
transform: translateX(0);
}
100% {
opacity: 0;
transform: translateX(0);
}
`;

export const Finish = styled(ProgressBar)<{ $animationTime: number }>`
animation: ${fillOut} ${(props) => props.$animationTime}ms;
`;
44 changes: 44 additions & 0 deletions frontend/src/components/@common/PageLoadingBar/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { useEffect, useMemo } from 'react';
import { createPortal } from 'react-dom';
import { useRecoilValue } from 'recoil';
import { Finish, Progressing } from './PageLoadingBar.style';
import { isShowPageLoadingState } from 'store/atoms/@common';
import useToggle from 'hooks/@common/useToggle';

export const FINISH_ANIMATION_TIME = 600;

const PageLoadingBar = () => {
const isShowPageLoading = useRecoilValue(isShowPageLoadingState);

const { isOn: isShow, on: show, off: hide } = useToggle();
const { isOn: isShowFinish, on: showFinish, off: hideFinish } = useToggle();

const root = useMemo(() => document.getElementById('root')!, []);

useEffect(() => {
if (isShowPageLoading) {
show();
hideFinish();
return;
}

showFinish();
const hideId = setTimeout(hide, FINISH_ANIMATION_TIME / 2);
const hideFinishId = setTimeout(hideFinish, FINISH_ANIMATION_TIME);

return () => {
clearTimeout(hideId);
clearTimeout(hideFinishId);
};
}, [isShowPageLoading, show, hide, showFinish, hideFinish]);

return createPortal(
<>
{isShow && <Progressing />}
{isShowFinish && <Finish $animationTime={FINISH_ANIMATION_TIME} />}
</>,
root
);
};

export default PageLoadingBar;
18 changes: 18 additions & 0 deletions frontend/src/components/@common/PageLogger/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { PropsWithChildren, useEffect } from 'react';
import { useSetRecoilState } from 'recoil';
import { isShowPageLoadingState, lastPageState } from 'store/atoms/@common';

const PageLogger = (props: PropsWithChildren) => {
const { children } = props;
const setLastPage = useSetRecoilState(lastPageState);
const setIsShowPageLoadingState = useSetRecoilState(isShowPageLoadingState);

useEffect(() => {
setLastPage(children);
setIsShowPageLoadingState(false);
}, [children, setLastPage, setIsShowPageLoadingState]);

return children;
};

export default PageLogger;
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Fragment } from 'react';
import useShowState from 'hooks/@common/useShowState';
import { ContentBox, SeeMoreButton, SeeMoreButtonArea, Wrapper } from './SeeMoreContentBox.styles';
import useShowState from './hooks/useShowState';

interface SeeMoreContentBoxProps {
children: string;
Expand Down
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion frontend/src/mocks/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import gardenHandlers from './handlers/gardenHandlers';
import historyHandlers from './handlers/historyHandlers';

export const worker = setupWorker(
...makeHandler(0, 0),
...makeHandler(200, 0),
...historyHandlers,
...dictionaryPlantRegistrationHandlers,
...gardenHandlers
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { keyframes, styled } from 'styled-components';

export const Wrapper = styled.div`
export const Main = styled.main`
display: flex;
flex-direction: column;
align-items: center;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
import { useState } from 'react';
import InstallPrompt from 'components/@common/InstallPrompt';
import Navbar from 'components/@common/Navbar';
import PageLogger from 'components/@common/PageLogger';
import SearchBox from 'components/search/SearchBox';
import { LogoMessage, SearchBoxArea, SearchMessage, Wrapper, Image, ImageArea } from './Main.style';
import { LogoMessage, SearchBoxArea, SearchMessage, Main, Image, ImageArea } from './Home.style';
import useDictionaryNavigate from 'hooks/dictionaryPlant/useDictionaryPlantNavigate';
import LogoSvg from 'assets/logo.svg';
import LogoWebp from 'assets/logo.webp';

const Main = () => {
const Home = () => {
const { goToProperDictionaryPlantPage, goToDictionaryPlantDetailPage } = useDictionaryNavigate();
const [searchValue, setSearchValue] = useState('');

return (
<>
<PageLogger>
<InstallPrompt />
<Wrapper>
<Main>
<LogoMessage>식물을 쉽게</LogoMessage>
<ImageArea>
<picture>
Expand All @@ -32,10 +33,10 @@ const Main = () => {
/>
</SearchBoxArea>
<SearchMessage>피움에 등록된 식물을 검색해 보세요!</SearchMessage>
</Wrapper>
</Main>
<Navbar />
</>
</PageLogger>
);
};

export default Main;
export default Home;
21 changes: 21 additions & 0 deletions frontend/src/pages/@common/LastPageLoading/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { useEffect } from 'react';
import { useRecoilValue, useSetRecoilState } from 'recoil';
import Loading from 'pages/@common/Loading';
import { isShowPageLoadingState, lastPageState } from 'store/atoms/@common';

const LastPageLoading = () => {
const lastPage = useRecoilValue(lastPageState);
const setIsShowPageLoading = useSetRecoilState(isShowPageLoadingState);

useEffect(() => {
setIsShowPageLoading(true);

return () => {
setIsShowPageLoading(false);
};
}, [setIsShowPageLoading]);

return lastPage ?? <Loading />;
};

export default LastPageLoading;
4 changes: 2 additions & 2 deletions frontend/src/pages/@common/RootTemplate/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Suspense } from 'react';
import { Outlet } from 'react-router-dom';
import NotFound from 'pages/@common/Error/NotFound';
import Loading from 'pages/@common/Loading';
import LastPageLoading from 'pages/@common/LastPageLoading';
import ErrorBoundary from 'components/@common/ErrorBoundary';
import Redirect from 'components/@common/Redirect';
import { PageArea, Wrapper } from './RootTemplate.style';
Expand All @@ -23,7 +23,7 @@ const RootTemplate = () => {
}
statusCode={401}
>
<Suspense fallback={<Loading />}>
<Suspense fallback={<LastPageLoading />}>
<Outlet />
</Suspense>
</ErrorBoundary>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/pages/auth/MyPage/MyPage.style.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Link } from 'react-router-dom';
import styled from 'styled-components';

export const Wrapper = styled.main`
export const Main = styled.main`
position: relative;
display: flex;
Expand Down
11 changes: 6 additions & 5 deletions frontend/src/pages/auth/MyPage/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { FixedButtonArea } from 'pages/garden/GardenPostList/GardenPostList.style';
import ContentHeader from 'components/@common/ContentHeader';
import Navbar from 'components/@common/Navbar';
import PageLogger from 'components/@common/PageLogger';
import SvgFill from 'components/@common/SvgIcons/SvgFill';
import Toggle from 'components/@common/Toggle';
import VerticalDivider from 'components/@common/VerticalDivider/VerticalDivider.style';
Expand All @@ -11,7 +12,7 @@ import {
PushAlertContent,
PushAlertWrapper,
WarnParagraph,
Wrapper,
Main,
} from './MyPage.style';
import useConfirm from 'hooks/@common/useConfirm';
import usePushAlert from 'hooks/@common/usePushAlert';
Expand Down Expand Up @@ -43,9 +44,9 @@ const MyPage = () => {
};

return (
<>
<PageLogger>
<ContentHeader title="마이페이지" />
<Wrapper>
<Main>
<PushAlertWrapper>
<PushAlertContent>
<p>리마인더 알림 받기</p>
Expand Down Expand Up @@ -74,15 +75,15 @@ const MyPage = () => {
회원 탈퇴
</Button>
</ButtonBox>
</Wrapper>
</Main>
<Navbar />
<FixedButtonArea>
<BottomSheet to="https://forms.gle/rQUAi9GbVwrr7oG2A" target="blank">
<SvgFill icon="survey" color={theme.color.background} size={16} />
문의하기
</BottomSheet>
</FixedButtonArea>
</>
</PageLogger>
);
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { generatePath, useNavigate, useParams } from 'react-router-dom';
import { Header } from 'pages/petPlant/PetPlantRegister/Form/Form.style';
import Image from 'components/@common/Image';
import PageLogger from 'components/@common/PageLogger';
import SvgFill from 'components/@common/SvgIcons/SvgFill';
import DictionaryPlantContent from 'components/dictionaryPlant/DictionaryPlantContent';
import { BackButton, BottomSheet, Main, PrimaryButton } from './DictionaryPlantDetail.style';
Expand Down Expand Up @@ -46,7 +47,7 @@ const DictionaryPlantDetail = () => {
};

return (
<>
<PageLogger>
<Header>
<BackButton onClick={goBack}>
<SvgFill icon="line-arrow-left" aria-label="뒤로 가기" color={theme.color.sub} />
Expand All @@ -61,7 +62,7 @@ const DictionaryPlantDetail = () => {
반려 식물로 등록하기
</PrimaryButton>
</BottomSheet>
</>
</PageLogger>
);
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useState } from 'react';
import { useSearchParams } from 'react-router-dom';
import Navbar from 'components/@common/Navbar';
import PageLogger from 'components/@common/PageLogger';
import SearchBox from 'components/search/SearchBox';
import SearchResults from 'components/search/SearchResults';
import { Title, Wrapper } from './DictionaryPlantSearch.style';
Expand All @@ -14,7 +15,7 @@ const DictionarySearch = () => {
const [searchValue, setSearchValue] = useState('');

return (
<>
<PageLogger>
<Wrapper>
<SearchBox
value={searchValue}
Expand All @@ -27,7 +28,7 @@ const DictionarySearch = () => {
<SearchResults plantName={search} />
</Wrapper>
<Navbar />
</>
</PageLogger>
);
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useLocation } from 'react-router-dom';
import ContentHeader from 'components/@common/ContentHeader';
import Navbar from 'components/@common/Navbar';
import PageLogger from 'components/@common/PageLogger';
import { Description, Main } from './NewDictionaryPlantRequest.style';
import { NUMBER } from 'constants/index';
import Form from './Form';
Expand All @@ -11,14 +12,14 @@ const NewDictionaryPlantRequest = () => {
const initialName = typeof state === 'string' ? state.slice(0, NUMBER.maxNicknameLength) : '';

return (
<>
<PageLogger>
<ContentHeader title="새로운 식물 추가 요청하기" />
<Main>
<Description>요청하신 식물은 저희가 검토 후 추가할게요!</Description>
<Form initialName={initialName} />
</Main>
<Navbar />
</>
</PageLogger>
);
};

Expand Down
Loading

0 comments on commit 2d47cb9

Please sign in to comment.