Next.js 페이지 전환 시 화면 로드 중
Next.js 앱에서 경로를 변경할 때 로드 화면을 구현하려고 합니다(예: /home -> /about).
현재 실시 상황은 다음과 같습니다.초기 로드 상태를 false로 설정하고 componentDidMount에서 변경합니다.또한 루트 변경이 시작되면 componentDidMount 내의 Router.events.on 함수를 호출하여 로드 상태를 변경합니다.
페이지 폴더의 _app.parames
class MyApp extends App {
constructor(props) {
super(props);
this.state = {
loaded: false,
};
}
componentDidMount() {
this.setState({ loaded: true });
Router.events.on('routeChangeStart', () => this.setState({ loaded: false }));
Router.events.on('routeChangeComplete', () => this.setState({ loaded: true }));
}
render() {
const { Component, pageProps } = this.props;
const { loaded } = this.state;
const visibleStyle = {
display: '',
transition: 'display 3s',
};
const inVisibleStyle = {
display: 'none',
transition: 'display 3s',
};
return (
<Container>
<>
<span style={loaded ? inVisibleStyle : visibleStyle}>
<Loader />
</span>
<span style={loaded ? visibleStyle : inVisibleStyle}>
<Component {...pageProps} />
</span>
</>
</Container>
);
}
}
이 방법은 완벽하게 작동하지만 더 우아한 해결책이 있을 것 같습니다.이것이 이 로딩 기능을 구현하는데 번거롭지 않은 유일한 방법입니까, 아니면 다른 방법이 있습니까?
새로운 hook api를 사용하면 이렇게 할 수 있습니다.
function Loading() {
const router = useRouter();
const [loading, setLoading] = useState(false);
useEffect(() => {
const handleStart = (url) => (url !== router.asPath) && setLoading(true);
const handleComplete = (url) => (url === router.asPath) && setLoading(false);
router.events.on('routeChangeStart', handleStart)
router.events.on('routeChangeComplete', handleComplete)
router.events.on('routeChangeError', handleComplete)
return () => {
router.events.off('routeChangeStart', handleStart)
router.events.off('routeChangeComplete', handleComplete)
router.events.off('routeChangeError', handleComplete)
}
})
return loading && (<div>Loading....{/*I have an animation here*/}</div>);
}
지금이다<Loading/>
루트가 바뀔 때마다 나타나게 될 거야리액트 스프링을 사용하여 애니메이션을 만들지만 원하는 라이브러리를 사용할 수 있습니다.
한 걸음 더 나아가 컴포넌트가 표시되는 타이밍을 수정하려면handleStart
그리고.handleComplete
취득하는 메서드url
.
왜 사용하지 않는가?nprogress
에 나타난 바와 같이_app.js
import React from 'react';
import Router from 'next/router';
import App, { Container } from 'next/app';
import NProgress from 'nprogress';
NProgress.configure({ showSpinner: publicRuntimeConfig.NProgressShowSpinner });
Router.onRouteChangeStart = () => {
// console.log('onRouteChangeStart triggered');
NProgress.start();
};
Router.onRouteChangeComplete = () => {
// console.log('onRouteChangeComplete triggered');
NProgress.done();
};
Router.onRouteChangeError = () => {
// console.log('onRouteChangeError triggered');
NProgress.done();
};
export default class MyApp extends App { ... }
nprogress에 링크합니다.
스타일 파일도 포함해야 합니다.css 파일을 넣으면static
디렉토리에서는, 다음과 같이 스타일에 액세스 할 수 있습니다.
<link rel="stylesheet" type="text/css" href="/static/css/nprogress.css" />
모든 페이지에서 CSS를 사용할 수 있는지 확인합니다.
모든 루트가 변경되었을 때 사용할 수 있습니다.
2021년에 이것을 접하는 사람은, 패키지 nextjs-progressbar를 사용하면, 이것을 매우 간단하게 할 수 있습니다.Next.js에서_app.js
, 추가:
import NextNProgress from 'nextjs-progressbar';
export default function MyApp({ Component, pageProps }) {
return (
<>
<NextNProgress />
<Component {...pageProps} />;
</>
);
}
끝!
데모 및 스크린샷:
NProgress를 사용한 새 업데이트:
import Router from 'next/router'
import Link from 'next/link'
import Head from 'next/head'
import NProgress from 'nprogress'
Router.events.on('routeChangeStart', (url) => {
console.log(`Loading: ${url}`)
NProgress.start()
})
Router.events.on('routeChangeComplete', () => NProgress.done())
Router.events.on('routeChangeError', () => NProgress.done())
export default function App({ Component, pageProps }) {
return (
<>
<Head>
{/* Import CSS for nprogress */}
<link rel="stylesheet" type="text/css" href="/nprogress.css" />
</Head>
<Component {...pageProps} />
</>
)
}
Tailwind CSS를 사용하는 경우 https://unpkg.com/nprogress@0.2.0/nprogress.css에서 코드를 복사하여 글로벌 CSS 파일에 붙여넣습니다.
스피너를 비활성화하려면 아래 코드를 추가합니다._app.tsx/jsx
파일을 작성하여 CSS에서 스피너 스타일을 삭제합니다.
NProgress.configure({ showSpinner: false });
소스 링크:
코드 90 행의 NProgress 와 같은 프로그레스 바(vs NProgress v0.2.0 은 470 행.js + 70 행.css):
import { useEffect, useReducer, useRef } from 'react';
import { assert } from './assert';
import { wait } from './wait';
import { getRandomInt } from './getRandomNumber';
let waitController: AbortController | undefined;
// https://gist.github.com/tkrotoff/db8a8106cc93ae797ea968d78ea28047
export function useProgressBar({
trickleMaxWidth = 94,
trickleIncrementMin = 1,
trickleIncrementMax = 5,
dropMinSpeed = 50,
dropMaxSpeed = 150,
transitionSpeed = 600
} = {}) {
// https://stackoverflow.com/a/66436476
const [, forceUpdate] = useReducer(x => x + 1, 0);
// https://github.com/facebook/react/issues/14010#issuecomment-433788147
const widthRef = useRef(0);
function setWidth(value: number) {
widthRef.current = value;
forceUpdate();
}
async function trickle() {
if (widthRef.current < trickleMaxWidth) {
const inc =
widthRef.current +
getRandomInt(trickleIncrementMin, trickleIncrementMax); // ~3
setWidth(inc);
try {
await wait(getRandomInt(dropMinSpeed, dropMaxSpeed) /* ~100 ms */, {
signal: waitController!.signal
});
await trickle();
} catch {
// Current loop aborted: a new route has been started
}
}
}
async function start() {
// Abort current loops if any: a new route has been started
waitController?.abort();
waitController = new AbortController();
// Force the show the JSX
setWidth(1);
await wait(0);
await trickle();
}
async function complete() {
assert(
waitController !== undefined,
'You need to ensure start() is called before calling complete()'
);
setWidth(100);
try {
await wait(transitionSpeed, { signal: waitController.signal });
setWidth(0);
} catch {
// Current loop aborted: a new route has been started
}
}
function reset() {
// Abort current loops if any
waitController?.abort();
setWidth(0);
}
useEffect(() => {
return () => {
// Abort current loops if any
waitController?.abort();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return {
start,
complete,
reset,
width: widthRef.current
};
}
import { useRouter } from 'next/router';
import { useEffect } from 'react';
import { useProgressBar } from './useProgressBar';
const transitionSpeed = 600;
// https://gist.github.com/tkrotoff/db8a8106cc93ae797ea968d78ea28047
export function RouterProgressBar(
props?: Parameters<typeof useProgressBar>[0]
) {
const { events } = useRouter();
const { width, start, complete, reset } = useProgressBar({
transitionSpeed,
...props
});
useEffect(() => {
events.on('routeChangeStart', start);
events.on('routeChangeComplete', complete);
events.on('routeChangeError', reset); // Typical case: "Route Cancelled"
return () => {
events.off('routeChangeStart', start);
events.off('routeChangeComplete', complete);
events.off('routeChangeError', reset);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return width > 0 ? (
// Use Bootstrap, Material UI, Tailwind CSS... to style the progress bar
<div
className="progress fixed-top bg-transparent rounded-0"
style={{
height: 3, // GitHub turbo-progress-bar height is 3px
zIndex: 1091 // $zindex-toast + 1 => always visible
}}
>
<div
className="progress-bar"
style={{
width: `${width}%`,
//transition: 'none',
transition: `width ${width > 1 ? transitionSpeed : 0}ms ease`
}}
/>
</div>
) : null;
}
사용방법:
// pages/_app.tsx
import { AppProps } from 'next/app';
import Head from 'next/head';
import { RouterProgressBar } from './RouterProgressBar';
export default function App({ Component, pageProps }: AppProps) {
return (
<>
<Head>
<title>My title</title>
<meta name="description" content="My description" />
</Head>
<RouterProgressBar />
<Component {...pageProps} />
</>
);
}
자세한 사항은 이쪽:https://gist.github.com/tkrotoff/db8a8106cc93ae797ea968d78ea28047
언급URL : https://stackoverflow.com/questions/55624695/loading-screen-on-next-js-page-transition
'programing' 카테고리의 다른 글
Wordpress: get_post_field() 함수를 사용하여 게시 내용을 가져올 때 쇼트코드가 작동하지 않습니다. (0) | 2023.03.23 |
---|---|
ORA-01034: ORACLE을 사용할 수 없음 ORA-27101: 공유 메모리 영역이 없습니다. (0) | 2023.03.23 |
리액트 + 플럭스 및 서버 측 렌더링(이형 반응 + 플럭스) (0) | 2023.03.23 |
Angular에서 상위 범위 변수 업데이트JS (0) | 2023.03.18 |
봄 테스트용으로 메모리 데이터베이스에 특정 구성 (0) | 2023.03.18 |