-
Notifications
You must be signed in to change notification settings - Fork 7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
MyTemplatePage 초기 랜더링 Layoutshift 개선 #706
Merged
Merged
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
d400bdf
refactor(TagFilterMenu): height의 기본값 LINE_HEIGHT_REM 으로 설정
Hain-tain d9609d8
feat(templates): useTemplateCategoryTagQueries
Hain-tain cedc2ba
refactor(api): getTemplateList 에서 keyword 가 있는 경우에만 queryParams 에 포함되…
Hain-tain 0d83344
refactor(src): useTemplateCategoryTagQueries 적용 및 Suspense 설정
Hain-tain cfb88bc
refactor(Footer): isChecking 아닐때 랜더링되도록 수정
Hain-tain 0be7178
refactor(MyTemplatePage): templates, categories, tags => templateList…
Hain-tain 5074e61
refactor(routes): Suspense fallback 내부 Flex css props 삭제
Hain-tain 88b6c3f
feat(src): LoadingFallback 컴포넌트 생성 및 적용
Hain-tain b5215c6
Merge branch 'dev/fe' into refactor/695-perf-optimization
Hain-tain d62ccac
refactor(MyTemplatesPage): 사용하지 않는 import 제거
Hain-tain File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,23 +1,32 @@ | ||
import { Text } from '@/components'; | ||
import { useAuth } from '@/hooks/authentication'; | ||
|
||
import * as S from './Footer.style'; | ||
|
||
const Footer = () => ( | ||
<S.FooterContainer> | ||
<Text.Small color='inherit'> | ||
Copyright{' '} | ||
<Text.Small color='inherit' weight='bold'> | ||
Codezap | ||
</Text.Small>{' '} | ||
© All rights reserved. | ||
</Text.Small> | ||
<S.ContactEmail href='mailto:[email protected]'> | ||
<Text.Small color='inherit' weight='bold'> | ||
문의 : | ||
</Text.Small>{' '} | ||
<Text.Small color='inherit'>[email protected]</Text.Small>{' '} | ||
</S.ContactEmail> | ||
</S.FooterContainer> | ||
); | ||
const Footer = () => { | ||
const { isChecking } = useAuth(); | ||
|
||
if (isChecking) { | ||
return null; | ||
} | ||
|
||
return ( | ||
<S.FooterContainer> | ||
<Text.Small color='inherit'> | ||
Copyright{' '} | ||
<Text.Small color='inherit' weight='bold'> | ||
Codezap | ||
</Text.Small>{' '} | ||
© All rights reserved. | ||
</Text.Small> | ||
<S.ContactEmail href='mailto:[email protected]'> | ||
<Text.Small color='inherit' weight='bold'> | ||
문의 : | ||
</Text.Small>{' '} | ||
<Text.Small color='inherit'>[email protected]</Text.Small>{' '} | ||
</S.ContactEmail> | ||
</S.FooterContainer> | ||
); | ||
}; | ||
|
||
export default Footer; |
10 changes: 10 additions & 0 deletions
10
frontend/src/components/LoadingFallback/LoadingFallback.style.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import styled from '@emotion/styled'; | ||
|
||
export const FallbackContainer = styled.div` | ||
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
|
||
width: 100vw; | ||
height: 100vh; | ||
`; |
10 changes: 10 additions & 0 deletions
10
frontend/src/components/LoadingFallback/LoadingFallback.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import LoadingBall from '../LoadingBall/LoadingBall'; | ||
import * as S from './LoadingFallback.style'; | ||
|
||
const LoadingFallback = () => ( | ||
<S.FallbackContainer> | ||
<LoadingBall /> | ||
</S.FallbackContainer> | ||
); | ||
|
||
export default LoadingFallback; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,23 +3,10 @@ import { useNavigate } from 'react-router-dom'; | |
|
||
import { DEFAULT_SORTING_OPTION, SORTING_OPTIONS } from '@/api'; | ||
import { ArrowUpIcon, PlusIcon, SearchIcon } from '@/assets/images'; | ||
import { | ||
Flex, | ||
Heading, | ||
Input, | ||
PagingButtons, | ||
Dropdown, | ||
Button, | ||
Modal, | ||
Text, | ||
LoadingBall, | ||
NoSearchResults, | ||
} from '@/components'; | ||
import { Flex, Heading, Input, PagingButtons, Dropdown, Button, Modal, Text, NoSearchResults } from '@/components'; | ||
import { useWindowWidth, useDebounce, useToggle, useDropdown, useInput } from '@/hooks'; | ||
import { useAuth } from '@/hooks/authentication'; | ||
import { useCategoryListQuery } from '@/queries/categories'; | ||
import { useTagListQuery } from '@/queries/tags'; | ||
import { useTemplateDeleteMutation, useTemplateListQuery } from '@/queries/templates'; | ||
import { useTemplateDeleteMutation, useTemplateCategoryTagQueries } from '@/queries/templates'; | ||
import { END_POINTS } from '@/routes'; | ||
import { theme } from '@/style/theme'; | ||
import { scroll } from '@/utils'; | ||
|
@@ -47,18 +34,17 @@ const MyTemplatePage = () => { | |
|
||
const [page, setPage] = useState<number>(1); | ||
|
||
const { data: templateData, isPending } = useTemplateListQuery({ | ||
const [{ data: templateData }, { data: categoryData }, { data: tagData }] = useTemplateCategoryTagQueries({ | ||
keyword: debouncedKeyword, | ||
categoryId: selectedCategoryId, | ||
tagIds: selectedTagIds, | ||
sort: sortingOption.key, | ||
page, | ||
}); | ||
const { data: categoryData } = useCategoryListQuery(); | ||
const { data: tagData } = useTagListQuery(); | ||
const templates = templateData?.templates || []; | ||
const categories = categoryData?.categories || []; | ||
const tags = tagData?.tags || []; | ||
|
||
const templateList = templateData?.templates || []; | ||
const categoryList = categoryData?.categories || []; | ||
const tagList = tagData?.tags || []; | ||
const totalPages = templateData?.totalPages || 0; | ||
|
||
const { mutateAsync: deleteTemplates } = useTemplateDeleteMutation(selectedList); | ||
|
@@ -84,13 +70,13 @@ const MyTemplatePage = () => { | |
}; | ||
|
||
const handleAllSelected = () => { | ||
if (selectedList.length === templates.length) { | ||
if (selectedList.length === templateList.length) { | ||
setSelectedList([]); | ||
|
||
return; | ||
} | ||
|
||
setSelectedList(templates.map((template) => template.id)); | ||
setSelectedList(templateList.map((template) => template.id)); | ||
}; | ||
|
||
const handleDelete = () => { | ||
|
@@ -100,11 +86,7 @@ const MyTemplatePage = () => { | |
}; | ||
|
||
const renderTemplateContent = () => { | ||
if (isPending) { | ||
return <LoadingBall />; | ||
} | ||
|
||
if (templates.length === 0) { | ||
if (templateList.length === 0) { | ||
if (debouncedKeyword !== '') { | ||
return <NoSearchResults />; | ||
} else { | ||
|
@@ -114,7 +96,7 @@ const MyTemplatePage = () => { | |
|
||
return ( | ||
<TemplateGrid | ||
templates={templates} | ||
templateList={templateList} | ||
cols={getGridCols(windowWidth)} | ||
isEditMode={isEditMode} | ||
selectedList={selectedList} | ||
|
@@ -128,7 +110,7 @@ const MyTemplatePage = () => { | |
<TopBanner name={name ?? '나'} /> | ||
<S.MainContainer> | ||
<Flex direction='column' gap='2.5rem' style={{ marginTop: '4.5rem' }}> | ||
<CategoryFilterMenu categories={categories} onSelectCategory={handleCategoryMenuClick} /> | ||
<CategoryFilterMenu categoryList={categoryList} onSelectCategory={handleCategoryMenuClick} /> | ||
</Flex> | ||
|
||
<Flex direction='column' width='100%' gap='1rem'> | ||
|
@@ -139,7 +121,7 @@ const MyTemplatePage = () => { | |
돌아가기 | ||
</Button> | ||
<Button variant='outlined' size='small' onClick={handleAllSelected}> | ||
{selectedList.length === templates.length ? '전체 해제' : '전체 선택'} | ||
{selectedList.length === templateList.length ? '전체 해제' : '전체 선택'} | ||
</Button> | ||
<Button | ||
variant={selectedList.length ? 'contained' : 'text'} | ||
|
@@ -174,45 +156,28 @@ const MyTemplatePage = () => { | |
getOptionLabel={(option) => option.value} | ||
/> | ||
</Flex> | ||
{tags.length !== 0 && ( | ||
<TagFilterMenu tags={tags} selectedTagIds={selectedTagIds} onSelectTags={handleTagMenuClick} /> | ||
{tagList.length !== 0 && ( | ||
<TagFilterMenu tagList={tagList} selectedTagIds={selectedTagIds} onSelectTags={handleTagMenuClick} /> | ||
)} | ||
{renderTemplateContent()} | ||
|
||
{templates.length !== 0 && ( | ||
{templateList.length !== 0 && ( | ||
<Flex justify='center' gap='0.5rem' margin='1rem 0'> | ||
<PagingButtons currentPage={page} totalPages={totalPages} onPageChange={handlePageChange} /> | ||
</Flex> | ||
)} | ||
</Flex> | ||
|
||
{isDeleteModalOpen && ( | ||
<Modal isOpen={isDeleteModalOpen} toggleModal={toggleDeleteModal} size='xsmall'> | ||
<Flex direction='column' justify='space-between' align='center' margin='1rem 0 0 0' gap='2rem'> | ||
<Flex direction='column' justify='center' align='center' gap='0.75rem'> | ||
<Text.Large color='black' weight='bold'> | ||
정말 삭제하시겠습니까? | ||
</Text.Large> | ||
<Text.Medium color='black'>삭제된 템플릿은 복구할 수 없습니다.</Text.Medium> | ||
</Flex> | ||
<Flex justify='center' align='center' gap='0.5rem'> | ||
<Button variant='outlined' onClick={toggleDeleteModal}> | ||
취소 | ||
</Button> | ||
<Button onClick={handleDelete}>삭제</Button> | ||
</Flex> | ||
</Flex> | ||
</Modal> | ||
<ConfirmDeleteModal | ||
isDeleteModalOpen={isDeleteModalOpen} | ||
toggleDeleteModal={toggleDeleteModal} | ||
handleDelete={handleDelete} | ||
/> | ||
)} | ||
</S.MainContainer> | ||
|
||
<S.ScrollTopButton | ||
onClick={() => { | ||
scroll.top('smooth'); | ||
}} | ||
> | ||
<ArrowUpIcon aria-label='맨 위로' /> | ||
</S.ScrollTopButton> | ||
<ScrollTopButton /> | ||
</S.MyTemplatePageContainer> | ||
); | ||
}; | ||
|
@@ -246,3 +211,38 @@ const NewTemplateButton = () => { | |
}; | ||
|
||
export default MyTemplatePage; | ||
|
||
interface ConfirmDeleteModalProps { | ||
isDeleteModalOpen: boolean; | ||
toggleDeleteModal: () => void; | ||
handleDelete: () => void; | ||
} | ||
|
||
const ConfirmDeleteModal = ({ isDeleteModalOpen, toggleDeleteModal, handleDelete }: ConfirmDeleteModalProps) => ( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 요 모달은 따로 서브 컴포넌트로 파일 분리 안해도 괜찮을까요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 앗, 서브컨포넌트 분리하던거 다 reset 한줄 알았는데 아래 남아있었군요! |
||
<Modal isOpen={isDeleteModalOpen} toggleModal={toggleDeleteModal} size='xsmall'> | ||
<Flex direction='column' justify='space-between' align='center' margin='1rem 0 0 0' gap='2rem'> | ||
<Flex direction='column' justify='center' align='center' gap='0.75rem'> | ||
<Text.Large color='black' weight='bold'> | ||
정말 삭제하시겠습니까? | ||
</Text.Large> | ||
<Text.Medium color='black'>삭제된 템플릿은 복구할 수 없습니다.</Text.Medium> | ||
</Flex> | ||
<Flex justify='center' align='center' gap='0.5rem'> | ||
<Button variant='outlined' onClick={toggleDeleteModal}> | ||
취소 | ||
</Button> | ||
<Button onClick={handleDelete}>삭제</Button> | ||
</Flex> | ||
</Flex> | ||
</Modal> | ||
); | ||
|
||
const ScrollTopButton = () => ( | ||
<S.ScrollTopButton | ||
onClick={() => { | ||
scroll.top('smooth'); | ||
}} | ||
> | ||
<ArrowUpIcon aria-label='맨 위로' /> | ||
</S.ScrollTopButton> | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
3 changes: 2 additions & 1 deletion
3
frontend/src/pages/MyTemplatesPage/components/TagFilterMenu/TagFilterMenu.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
해당 suspense 처리만 해주어도 footer 관련 Layout Shift가 해결이 되나요??
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 부분은 Header와 동일한 로직으로 변경한 것인데요, 로그인한 유저인지 아닌지 검사하는동안 footer를 null 로 처리했다가 나중에 return 해주었습니다.
(footer는 언제나 맨 하단에 위치하기 때문에 나중에 떠도 Layout Shift가 발생하지 않을 것이라 생각하였습니다.)
이렇게 하면 Layout Shift을 완벽하게 해결할 순 없지만 처음에 너무 빨리 렌더링 되는 것을 막아 깜빡이는 현상을 없앨 수 있었습니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
괜찮은 방법이라고 생각되네요! 더 좋은 방법이 떠오르지 않습니당