사이드바 on/off 기능은 SideBar 컴포넌트에서
const [toggle, setToggle] = useRecoilState(sidebarToggle);
<button
type="button"
className={`side_toggle ${!toggle && 'close'}`}
onClick={() => setToggle(!toggle)}
>
간단하게 toggle 불리언값에 따라 조건부 렌더링으로 구현하였습니다.
이 sidebarToggle state는 다른 컴포넌트에서도 사용하기 때문에
Recoil을 사용해 전역관리 하였습니다.
sidebar가 생기거나 사라질 때 위젯들이 차지하는 레이아웃의 크기가 달라지기 때문에
WidgetLayout 컴포넌트에서 sidebarToggle state를 위젯들의 크기를 조정하는데 사용하였습니다.
const sidebarToggleState = useRecoilValue(sidebarToggle);
const [dynamicWidth, setDynamicWidth] = useState(
Math.max(windowWidth - 330, 1600)
);
useEffect(() => {
if (sidebarToggleState) {
setDynamicWidth(Math.max(windowWidth - 330, 1600));
} else {
setDynamicWidth(Math.max(windowWidth - 35, 1600));
}
}, [sidebarToggleState]);
탑바가 활성화된 상태 비활성화된 상태로 나뉘며 탑바 on/off에 따라 위젯들의
높이가 달라집니다. 마찬가지로 WidgetLayout 컴포넌트에서 관리하였습니다.
const [topSummaryOpen, setTopSummaryOpen] = useRecoilState(isTopModalState);
const [toggle, setToggle] = useState(topSummaryOpen);
const [dynamicHeight, setDynamicHeight] = useState(
Math.max((windowHeight - 118) / 4, 206.69)
);
// 탑바 토클 버튼 이벤트 핸들러
const handleTopSummary = () => {
setTopSummaryOpen(!topSummaryOpen);
if (topSummaryOpen) {
setDynamicHeight(Math.max((windowHeight - 118) / 4, 206.69));
} else {
setDynamicHeight(Math.max((windowHeight - 185) / 4, 206.69));
}
};
<div key={currentIndexState}>탑바 코드</div>
탑바 최상위 요소에 key값을 부여해서 데이터가 바뀔때마다 리액트에서 요소룰 다시 생성하는 방법으로 애니메이션 적용을 하였습니다.
탑바 안에 있는 요소의 데이터가 바뀔경우
const [animationStates, setAnimationStates] = useState({
ani03state: true,
ani04state: true,
ani05state: true,
});
const aniHandler = (key) => {
setAnimationStates((prevStates) => ({
...prevStates,
[key]: false,
}));
setTimeout(() => {
setAnimationStates((prevStates) => ({
...prevStates,
[key]: true,
}));
}, 300);
};
// 현장 코드 데이터와 날씨, 디데이, 공정률 위젯 데이터
const { data: widgetData } = useQuery(
['WidgetList', userInfo.userId, selectedSiteCode],
() =>
getWidgetInfo(
userInfo.userId,
[selectedSiteCode],
['weather', 'dday', 'processrate']
),
{
onSuccess: (data) => {
if (
(topbarDataInit && pageInfo.pageType === 'site') ||
(topbarDataInit &&
pageInfo.pageType === 'region' &&
!circulationState)
) {
if (
data.siteList[0].widgetList[0].data.temperature !==
widgetListState[0]?.data?.temperature ||
data.siteList[0].widgetList[0].data.sky !==
widgetListState[0]?.data?.sky
) {
aniHandler('ani03state');
}
if (
data.siteList[0].widgetList[0].data.maxTemp !==
widgetListState[0]?.data?.maxTemp ||
data.siteList[0].widgetList[0].data.minTemp !==
widgetListState[0]?.data?.minTemp
) {
aniHandler('ani04state');
}
if (
data.siteList[0].widgetList[0].data.pm25Value !==
widgetListState[0]?.data?.pm25Value ||
data.siteList[0].widgetList[0].data.pm10Value !==
widgetListState[0]?.data?.pm10Value
) {
aniHandler('ani05state');
}
}
setTopbarDataInit(true);
},
refetchInterval: 30 * 60 * 1000,
// cacheTime: 6000 * 1000
// refetchInterval: 6000 * 1000,
}
);
{animationStates.ani04state ? (
<div className="temp ani ani04">
<div>
<strong>
{widgetListState && widgetListState[0]?.data?.maxTemp}℃
</strong>
<span className="high">최고</span>
</div>
<div>
<strong>
{widgetListState && widgetListState[0]?.data?.minTemp}℃
</strong>
<span className="low">최저</span>
</div>
</div>
) : (
<div className="temp">
<div>
<strong>
{widgetListState && widgetListState[0]?.data?.maxTemp}℃
</strong>
<span className="high">최고</span>
</div>
<div>
<strong>
{widgetListState && widgetListState[0]?.data?.minTemp}℃
</strong>
<span className="low">최저</span>
</div>
</div>
)}
리액트 쿼리를 사용해 30분 마다 데이터를 받아올 때 이전값과 받아온 데이터 값을 비교해 해당 컴포넌트에 조건부 렌더링을 걸어서 애니메이션 효과가 다시 적용되도록 구현하였습니다.