-
Notifications
You must be signed in to change notification settings - Fork 4
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
사이드바에 토스트가 가려지던 문제 해결 (feat.createPortal) #841
Changes from all commits
973d0c7
dbde78c
c67edd2
965f0ba
0bf256b
d1a526b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -54,5 +54,6 @@ | |
</head> | ||
<body> | ||
<div id="root"></div> | ||
<div id="toast-content"></div> | ||
</body> | ||
</html> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { HTMLAttributes } from 'react'; | ||
|
||
import { ToastContentId } from '@type/toast'; | ||
|
||
import * as S from './style'; | ||
|
||
interface DrawerToastWrapperProps extends HTMLAttributes<HTMLDivElement> { | ||
id: ToastContentId; | ||
placement: 'left' | 'right'; | ||
} | ||
|
||
export default function DrawerToastWrapper({ placement, ...rest }: DrawerToastWrapperProps) { | ||
return <S.ToastWrapper {...rest} $placement={placement} />; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import { styled } from 'styled-components'; | ||
|
||
export const ToastWrapper = styled.div<{ $placement: 'left' | 'right' }>` | ||
position: absolute; | ||
width: 100vw; | ||
left: ${({ $placement }) => ($placement === 'left' ? '0' : 'auto')}; | ||
right: ${({ $placement }) => ($placement === 'right' ? '0' : 'auto')}; | ||
`; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,7 @@ | ||
import { PropsWithChildren, createContext, useEffect, useRef, useState } from 'react'; | ||
import { PropsWithChildren, createContext, useCallback, useEffect, useRef, useState } from 'react'; | ||
import { createPortal } from 'react-dom'; | ||
|
||
import { ToastContentId } from '@type/toast'; | ||
|
||
import ToastContainer from '@components/ToastContainer'; | ||
|
||
|
@@ -11,17 +14,32 @@ export interface ToastInfo { | |
|
||
interface ToastContextProps { | ||
addMessage: (message: string) => void; | ||
setElementId: (id: ToastContentId) => void; | ||
} | ||
|
||
export const ToastContext = createContext<ToastContextProps>({ | ||
addMessage: (message: string) => {}, | ||
setElementId: () => {}, | ||
}); | ||
|
||
export default function ToastProvider({ children }: PropsWithChildren) { | ||
const [toastList, setToastList] = useState<ToastInfo[]>([]); | ||
const [toastElementId, setToastElementId] = useState<ToastContentId>('toast-content'); | ||
const toastContentEl = document.getElementById(toastElementId); | ||
|
||
const timeId = useRef<number | null>(null); | ||
|
||
const addMessage = (message: string) => { | ||
if (toastList.find(toast => toast.text === message)) return; | ||
|
||
const id = Date.now(); | ||
setToastList(toastList => [...toastList, { id, text: message }]); | ||
}; | ||
|
||
const setElementId = useCallback((id: ToastContentId) => { | ||
setToastElementId(id); | ||
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. 리렌더링 방지👍👍 |
||
}, []); | ||
|
||
useEffect(() => { | ||
if (timeId.current) window.clearTimeout(timeId.current); | ||
|
||
|
@@ -34,16 +52,9 @@ export default function ToastProvider({ children }: PropsWithChildren) { | |
} | ||
}, [toastList]); | ||
|
||
const addMessage = (message: string) => { | ||
if (toastList.find(toast => toast.text === message)) return; | ||
|
||
const id = Date.now(); | ||
setToastList(toastList => [...toastList, { id, text: message }]); | ||
}; | ||
|
||
return ( | ||
<ToastContext.Provider value={{ addMessage }}> | ||
<ToastContainer toastList={toastList} /> | ||
<ToastContext.Provider value={{ addMessage, setElementId }}> | ||
{toastContentEl && createPortal(<ToastContainer toastList={toastList} />, toastContentEl)} | ||
{children} | ||
</ToastContext.Provider> | ||
); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,11 +14,11 @@ export const useReportApproveResult = (reportId: number) => { | |
suspense: true, | ||
|
||
retry: (failCount, error) => { | ||
// const fetchError = error as Error; | ||
// const status = JSON.parse(fetchError.message).status; | ||
// if (status === 404) { | ||
// return false; | ||
// } | ||
const fetchError = error as Error; | ||
const status = JSON.parse(fetchError.message).status; | ||
if (status === 404) { | ||
return false; | ||
} | ||
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. 👍👍👍 |
||
return failCount <= 3; | ||
}, | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,6 +14,7 @@ import AddButton from '@components/common/AddButton'; | |
import AppInstallPrompt from '@components/common/AppInstallPrompt'; | ||
import Dashboard from '@components/common/Dashboard'; | ||
import Drawer from '@components/common/Drawer'; | ||
import DrawerToastWrapper from '@components/common/Drawer/DrawerToastWrapper'; | ||
import Layout from '@components/common/Layout'; | ||
import NarrowMainHeader from '@components/common/NarrowMainHeader'; | ||
import Skeleton from '@components/common/Skeleton'; | ||
|
@@ -45,6 +46,7 @@ export default function HomePage() { | |
closeDrawer: closeAlarmDrawer, | ||
} = useDrawer('right'); | ||
|
||
const { setElementId } = useContext(ToastContext); | ||
const { isBannerOpen, closeBanner } = useBannerToggle(); | ||
const { addMessage } = useContext(ToastContext); | ||
const loggedInfo = useContext(AuthContext).loggedInfo; | ||
|
@@ -55,15 +57,31 @@ export default function HomePage() { | |
if (!loggedInfo.isLoggedIn) return addMessage('알림은 로그인 후 이용할 수 있습니다.'); | ||
|
||
openAlarmDrawer(); | ||
setElementId('drawer-alarm-toast-content'); | ||
mutate(); | ||
}; | ||
|
||
const handleAlarmDrawerClose = () => { | ||
closeAlarmDrawer(); | ||
setElementId('toast-content'); | ||
}; | ||
|
||
const handleCategoryDrawerOpen = () => { | ||
openCategoryDrawer(); | ||
setElementId('drawer-category-toast-content'); | ||
}; | ||
|
||
const handleCategoryDrawerClose = () => { | ||
closeCategoryDrawer(); | ||
setElementId('toast-content'); | ||
}; | ||
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. 여기까지 오기 위한 빌드업이었군요! |
||
|
||
return ( | ||
<Layout isSidebarVisible={true} isMobileDefaultHeaderVisible={false}> | ||
<S.Container> | ||
<S.HeaderWrapper> | ||
<NarrowMainHeader | ||
handleCategoryOpenClick={openCategoryDrawer} | ||
handleCategoryOpenClick={handleCategoryDrawerOpen} | ||
handleAlarmOpenClick={handleToolTipOpen} | ||
isAlarmActive={isAlarmActive ?? false} | ||
/> | ||
|
@@ -79,21 +97,23 @@ export default function HomePage() { | |
)} | ||
<S.DrawerWrapper> | ||
<Drawer | ||
handleDrawerClose={closeCategoryDrawer} | ||
handleDrawerClose={handleCategoryDrawerClose} | ||
placement="left" | ||
width="225px" | ||
ref={categoryDrawerRdf} | ||
> | ||
<DrawerToastWrapper placement="left" id="drawer-category-toast-content" /> | ||
<Dashboard /> | ||
</Drawer> | ||
<Drawer | ||
handleDrawerClose={closeAlarmDrawer} | ||
handleDrawerClose={handleAlarmDrawerClose} | ||
placement="right" | ||
width="310px" | ||
ref={alarmDrawerRef} | ||
> | ||
<DrawerToastWrapper id="drawer-alarm-toast-content" placement="right" /> | ||
{loggedInfo.isLoggedIn && ( | ||
<AlarmContainer closeToolTip={closeAlarmDrawer} style={alarmDrawerStyle} /> | ||
<AlarmContainer closeToolTip={handleAlarmDrawerClose} style={alarmDrawerStyle} /> | ||
)} | ||
</Drawer> | ||
</S.DrawerWrapper> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
export type ToastContentId = | ||
| 'toast-content' | ||
| 'drawer-category-toast-content' | ||
| 'drawer-alarm-toast-content'; | ||
|
||
export type DrawerToastContentId = Exclude<ToastContentId, 'toast-content'>; |
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.
다시 읽으며 든 생각인데,
document.getElementById
로 dom을 직접 제어하는 것은 지양된다고 알고 있습니다.real dom 과 가상돔이 달라지면서 돔 요소를 신뢰할 수 없어진다고 알고 있습니다.
위험부담을 감안하여 진행해야 할까요? 고민이 듭니다!
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.
이 부분은 잘 모르겠네요.
다만 공식문서에서 있는 방법을 참고하여서 진행했기 때문에 괜찮지 않을까라는 생각이 듭니다