Я собираюсь поделиться простым подходом к переходам страниц в Next.js на основе хуков. Эта статья посвящена не CSS, а тому, как писать пользовательские хуки react.
Для выполнения CSS-магии мы будем использовать https://mui.com/material-ui/transitions/.
Первым шагом будет определение способа перехвата рендеринга страницы в Next.js, что можно сделать, создав файл _app.js
в папке pages
.
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}
export default MyApp
https://nextjs.org/docs/advanced-features/custom-app
Next.js использует компонент App для инициализации страниц. Вы можете переопределить его и управлять инициализацией страницы.
Нас интересует только то, что _app.js
будет запускаться перед каждым рендерингом страницы, что позволит нам переопределить макет и включить эффекты перехода.
С этой информацией мы можем приступить к написанию нашего пользовательского хука.
import { useEffect, useState } from 'react';
export default function useSimpleRouteTransition({ delay, children }) {
const [transitionEnd, setTransitionEnd] = useState(true);
const [displayChildren, setDisplayChildren] = useState(children);
useEffect(() => {
setTransitionEnd(false);
const t = setTimeout(() => {
setDisplayChildren(children);
setTransitionEnd(true);
}, delay);
return () => {
clearTimeout(t);
};
}, [children]);
return {
displayChildren,
transitionEnd,
};
}
Для инициализации требуется два параметра:
Давайте проанализируем код.
const [transitionEnd, setTransitionEnd] = useState(true);
const [displayChildren, setDisplayChildren] = useState(children);
Мы определяем внутреннее состояние с true
в качестве начального значения и создаем копию children
.
Погружаемся в код useEffect
.
useEffect(() => {
setTransitionEnd(false);
const t = setTimeout(() => {
setDisplayChildren(children);
setTransitionEnd(true);
}, delay);
return () => {
clearTimeout(t);
};
}, [children]);
Каждый раз, когда children
меняется, ставится в очередь setTimeout
, который обновляет новых детей после установленной нами delay
. Чтобы представить это действие, мы также переключим наш внутренний transitionEnd
с false
на true
.
Наконец, таймаут очищается всякий раз, когда компонент размонтируется.
Если собрать все вместе в компонент Layout
, он должен выглядеть следующим образом:
import Link from 'next/link';
import { Box, Container, Stack, Fade } from '@mui/material';
import useSimpleRouteTransition from '@/hooks/useSimpleRouteTransition';
export default function Layout({ children }) {
const { transitionEnd, displayChildren } = useSimpleRouteTransition({
delay: 1000,
children,
});
return (
<Container maxWidth="lg">
<Box
sx={{
flexFlow: 'column nowrap',
}}
>
<Box mt={10} mb={0}>
<h1>Page transitions with Next.js</h1>
</Box>
</Box>
<Stack direction={'row'} spacing={2}>
<Link href="/">index</Link>
<Link href="/blog">blog</Link>
<Link href="/links">Links</Link>
</Stack>
<Box sx={{ bgcolor: 'green', p: 2 }}>
<Fade in={transitionEnd} timeout={1000}>
<div>{displayChildren}</div>
</Fade>
</Box>
<Box sx={{ bgcolor: 'darkblue', p: 2 }}>Footer</Box>
</Container>
);
}
Давайте рассмотрим реализацию.
const { transitionEnd, displayChildren } = useSimpleRouteTransition({
delay: 1000,
children,
});
Мы вызываем наш пользовательский хук с delay: 1000
и children
, которые мы получаем от нашего родительского компонента. Оттуда мы получаем displayChildren
и transitionEnd
.
<Fade in={transitionEnd} timeout={1000}>
<div>{displayChildren}</div>
</Fade>
В нашем представлении всегда показывается displayChildren
вместо children
. Мы обернули это представление в компонент Fade, который мы устанавливаем с помощью transitionEnd
для достижения управляемого затухания.
Вот и все! Дайте мне знать, если это работает для вас.
Вы можете найти весь исходный код на GitHub:
https://github.com/calinalexandru/next-js-router-transitions