728x90
✈️path를 따라가는 로딩화면 구현하기
import React from 'react';
type LoadingProps = {
width?: number;
};
export default function Loading({ width = 1200 }: LoadingProps) {
return (
<div className="flex items-center justify-center">
<svg
xmlns="http://www.w3.org/2000/svg"
width={width}
viewBox="0 0 400 200"
>
<defs>
<path
id="flightPath"
d="M 0 100 C 100 50, 300 150, 400 100"
fill="none"
stroke="lightgrey"
strokeWidth="2"
/>
</defs>
{/* 비행 경로를 따라 선을 그리기 위한 경로 */}
<path
id="line"
d="M 0 100 C 100 50, 300 150, 400 100"
fill="none"
stroke="lightblue"
strokeWidth="2"
>
<animate
attributeName="stroke-dasharray"
from="0,400"
to="440,0"
dur="3s"
repeatCount="indefinite"
/>
</path>
{/* 비행기 모양 */}
<path
id="plane"
d="M 27,3 H 21 L 13,15 H 9 L 12,3 H 5 L 3,7 H -1 L 1,0 -1,-7 H 3 L 5,-3 H 12 L 9,-15 H 13 L 21,-3 H 27 C 33,-3 33,3 27,3 Z"
fill="white"
stroke="black"
strokeWidth="1.5"
>
<animateMotion
rotate="auto"
begin="0s"
dur="3s"
repeatCount="indefinite"
>
<mpath xlinkHref="#flightPath" />
</animateMotion>
</path>
</svg>
</div>
);
}
useLoading
- routeChangeStart(url, { shallow }) - 라우트가 변경되기 시작할때 트리거됨.
- routeChangeComplete(url, { shallow }) - 라우트가 완전히 변경되었을 때 트리거됨.
- routeChangeError(err, url, { shallow }) - 라우트 변경 중에 에러가 발생했거나, 취소되었을 때 트리거됨.
// useLoading
import { Router } from 'next/router';
import { useEffect, useState } from 'react';
export const useLoading = () => {
const [loading, setLoading] = useState(false);
useEffect(() => {
const start = () => {
setLoading(true);
};
const end = () => {
setLoading(false);
};
Router.events.on('routeChangeStart', start);
Router.events.on('routeChangeComplete', end);
Router.events.on('routeChangeError', end);
return () => {
Router.events.off('routeChangeStart', start);
Router.events.off('routeChangeComplete', end);
Router.events.off('routeChangeError', end);
};
}, []);
return loading ? true : false;
};
레이아웃에 페이지 전환시 로딩 화면 띄우고 이동
export default function Layout({
children,
showHeader = true,
showFooter = true,
}: LayoutProps) {
const pathname = usePathname();
const router = useRouter();
const loading = useLoading();
return (
<>
{showHeader && <Header />}
<AnimatePresence mode="wait">
<motion.div
key={router.pathname}
initial={isMyPage ? undefined : { opacity: 0, y: -50 }}
animate={isMyPage ? undefined : { opacity: 1, y: 0 }}
exit={isMyPage ? undefined : { opacity: 0, y: -50 }}
transition={{ duration: 0.3 }}
>
{loading ? <Loading /> : children}
{!loading && showFooter && <Footer />}
</motion.div>
</AnimatePresence>
</>
);
}
하지만 모든 페이지이동에 넣으니 빠른 화면 전환사이에 로딩화면이 있어서 짜침
페이지라우터는 초기 페이지 로딩만 느리고, 캐시된 이후에는 엄청 빠르다.
각 페이지를 처음 방문할때만 로딩화면을 넣고, 캐시된 이후에는 로딩화면 없애기!!
import { Router } from 'next/router';
import { useEffect, useState } from 'react';
export const useLoading = () => {
const [showLoading, setShowLoading] = useState(true); // 처음에 로딩 화면을 보여줌
const [hasLoadedPages, setHasLoadedPages] = useState(new Set()); // 로드된 페이지를 추적
useEffect(() => {
const start = (url: string) => {
if (hasLoadedPages.has(url)) return; // 이미 로드된 페이지에서는 로딩 시작하지 않음
setShowLoading(true); // 로딩 화면 표시
};
const end = (url: string) => {
setShowLoading(false); // 로딩 종료 시 showLoading을 false로
setHasLoadedPages((prev) => new Set(prev).add(url)); // 현재 페이지를 로드된 페이지에 추가
};
Router.events.on('routeChangeStart', start);
Router.events.on('routeChangeComplete', end);
Router.events.on('routeChangeError', end);
return () => {
Router.events.off('routeChangeStart', start);
Router.events.off('routeChangeComplete', end);
Router.events.off('routeChangeError', end);
};
}, [hasLoadedPages]);
return showLoading; // showLoading을 반환
};
완성
'🔥React 뽀개기' 카테고리의 다른 글
React Native와 Expo로 간편하게 앱 개발하기 (1) | 2025.02.04 |
---|---|
페이지네이션 초간단 구현하기 (React) (2) | 2024.12.23 |
Toast 공통 컴포넌트 만들기 (feat.react-toastify) (0) | 2024.08.08 |
react-hook-form + yup = 회원가입/로그인(feat.쿠키) (0) | 2024.08.02 |
input 공통 컴포넌트로 만들기 (feat. react-hook-form + yup) (0) | 2024.08.01 |