#reactjs #react-hooks #gatsby #use-effect #use-state
#reactjs #реагирующие крючки #gatsby #использование-эффект #use-state
Вопрос:
Я пытаюсь реализовать react-transition-group
, и мне нужно иметь возможность изменять состояние fadeEffectVisible
от false
до true
, предполагая, что путь, по которому переходит пользователь, не совпадает с текущим путем. Другими словами, это должно работать, если пользователь переходит от page-1
к page-2
, но не если пользователь включен page-1
и нажимает на ссылку page-1
. Я использую хуки и функциональные компоненты, и это мой AppLayout
компонент в его нынешнем виде.
import React, { ReactNode, useEffect, useState } from 'react';
import { Footer } from 'components/footer/Footer';
import { Header } from 'components/header/Header';
import { NavigationSkipLink } from 'components/navigation-skip-link/NavigationSkipLink';
import { AppContext } from 'contexts/app-context/AppContext';
import { graphql, StaticQuery } from 'gatsby';
import { TransitionGroup, CSSTransition } from 'react-transition-group';
import s from './AppLayout.scss';
interface AppLayoutProps {
children: ReactNode;
location: any;
}
export const MainContentId = 'maincontent';
const NavQuery = graphql`
query NavQuery {
prismic {
allNavigations {
edges {
node {
...NotificationBar
...NavigationItems
...FooterNavigationItems
...LegalNavigationItems
}
}
}
}
}
`;
// eslint-disable-next-line react/display-name
export default ({ children, location }: AppLayoutProps) => {
const prevPath = location.href;
const [fadeEffectVisible, setfadeEffectVisible] = useState(true);
const handleFadeEffectEntered = () => {
setTimeout(() => {
setfadeEffectVisible(false);
}, 50);
};
useEffect(() => {
if (prevPath !== path) {
setfadeEffectVisible(true);
} else {
setfadeEffectVisible(false);
}
}, [path]);
return (
<StaticQuery
query={`${NavQuery}`}
render={(data) => (
<>
<AppContext>
<NavigationSkipLink />
<Header navigationContent={data.prismic.allNavigations.edges[0].node} />
<CSSTransition
in={fadeEffectVisible}
timeout={150}
classNames={{
enter: s.fadeEffectEnter,
enterActive: s.fadeEffectEnterActive,
enterDone: s.fadeEffectEnterDone,
exit: s.fadeEffectExit,
exitActive: s.fadeEffectExitActive,
}}
onEntered={handleFadeEffectEntered}
>
<div className={s.fadeEffect} aria-hidden="true" />
</CSSTransition>
<TransitionGroup component={null}>
<CSSTransition
key={path}
timeout={150}
classNames={{
enter: s.pageEnter,
}}
>
<div id={MainContentId} className={s.layout}>
{children}
<Footer navigationItems={data.prismic.allNavigations.edges[0].node} />
</div>
</CSSTransition>
</TransitionGroup>
</AppContext>
</>
)}
/>
);
};
Я даже близок к тому, чтобы быть на правильном пути здесь? Спасибо!
Комментарии:
1.
path !== path
всегда будет false . Вам нужно сравнить текущее значение пути с предыдущим значением пути? Как получить предыдущие реквизиты или состояние? Или скорее вам нужно протестировать целевой путь по текущему пути?2. Мне нужно проверить целевой путь на соответствие текущему пути.
3.Итак, проверка чего-то вроде
targetPath !== path
перед навигацией? Можете ли вы просто полностью запретить навигацию, если она уже находится на этом пути? Я думаю, нам нужно немного больше контекста кода. Можете ли вы предоставить более полный пример кода компонента?4. Вопрос обновлен дополнительным кодом. Спасибо, что взглянули.
5. Я думаю, вы можете быть близки, но неясно, где
path
определено, откуда оно взялось. При условии, что обаpath
параметра иprevPath
и зависимость эффекта верны, более лаконичной реализацией будетsetfadeEffectVisible(prevPath !== path)
int he effect . Это приведет к задержкеfadeEffectVisible
значения на цикл рендеринга, и я не уверен, что это именно то, что вам нужно. Значение состояния, по-видимому, просто является производным значением от обоихprevPath
иpath
, поэтому, возможно, просто вычислите это в текущем цикле рендеринга, т.Е.in={prevPath !== path}
?
Ответ №1:
Я бы использовал другой подход, используя useRef
hook для сохранения предыдущего пути страницы (сохраненного в a useState
) и сравнения между ними.
Создание пользовательского хука типа:
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
});
return ref.current;
}
И добавьте следующие строки в свой компонент:
const [page, setPage] = useState(location.pathname);
const prevPage = usePrevious(page) // <-- look here for your previous page
После этого вам нужно только сравнить location.pathname !== prevPage
.
location
prop
является prop
открытым Gatsby (потому что он распространяется @reach/router
из React) только в компонентах (страницах) верхнего уровня. Возможно pathname
, свойство не совсем соответствует вашим потребностям, проверьте его, чтобы узнать, какое свойство соответствует вашим требованиям.
Ссылки:
Комментарии:
1. Спасибо, это здорово. Однако я столкнулся с небольшой проблемой, которая заключается в том, что
const prevPage = usePrevious(page)
никогда не обновляется до самой ближайшей предыдущей страницы; он возвращает только первуюstate
.2. Возможно, вы можете еще немного поиграть с хуками (не нарушая правил хуков). Попробуйте создать a
useEffect
с местоположением в качестве зависимости, чтобы запустить вашusePrevious
.
Ответ №2:
Понял это, благодаря большой помощи от ответа Феррана, но я хотел поделиться своим полным компонентом теперь, когда он у меня работает. В частности, именно так я его настроил, чтобы правильно обновлять мое page
состояние и проверять предыдущее url
на соответствие целевому.
const [page, setPage] = useState(pathname);
const prevPage = usePrevious(pathname);
useEffect(() => {
if (pathname !== prevPage) {
setFadeEffectVisible(true);
setPage(page);
}
}, [pathname]);
AppLayout.tsx
import React, { ReactNode, useEffect, useState } from 'react';
import { Devtools } from 'components/devtools/Devtools';
import { Footer } from 'components/footer/Footer';
import { Header } from 'components/header/Header';
import { NavigationSkipLink } from 'components/navigation-skip-link/NavigationSkipLink';
import { AppContext } from 'contexts/app-context/AppContext';
import { graphql, StaticQuery } from 'gatsby';
import { usePrevious } from 'hooks/use-previous';
import { TransitionGroup, CSSTransition } from 'react-transition-group';
import s from './AppLayout.scss';
interface AppLayoutProps {
props: any;
children: ReactNode;
location: any;
}
const isDev = process.env.NODE_ENV === 'development';
export const MainContentId = 'maincontent';
const NavQuery = graphql`
query NavQuery {
prismic {
allNavigations {
edges {
node {
...NotificationBar
...NavigationItems
...FooterNavigationItems
...LegalNavigationItems
}
}
}
}
}
`;
// eslint-disable-next-line react/display-name
export default ({ children, location: { pathname } }: AppLayoutProps) => {
const [fadeEffectVisible, setFadeEffectVisible] = useState(false);
const [page, setPage] = useState(pathname);
const prevPage = usePrevious(pathname);
const timeout = 250;
useEffect(() => {
if (pathname !== prevPage) {
setFadeEffectVisible(true);
setPage(page);
}
}, [pathname]);
console.log(prevPage);
const handleFadeEffectEntered = () => {
setTimeout(() => {
setFadeEffectVisible(false);
}, 50);
};
return (
<StaticQuery
query={`${NavQuery}`}
render={(data) => (
<>
<AppContext>
<CSSTransition
in={fadeEffectVisible}
timeout={timeout}
classNames={{
enter: s.fadeEffectEnter,
enterActive: s.fadeEffectEnterActive,
enterDone: s.fadeEffectEnterDone,
exit: s.fadeEffectExit,
exitActive: s.fadeEffectExitActive,
}}
onEntered={handleFadeEffectEntered}
>
<div className={s.fadeEffect} aria-hidden="true" />
</CSSTransition>
<NavigationSkipLink />
<Header navigationContent={data.prismic.allNavigations.edges[0].node} />
<TransitionGroup component={null}>
<CSSTransition
key={pathname}
timeout={timeout}
classNames={{
enter: s.pageEnter,
}}
>
<div id={MainContentId} className={s.layout}>
{children}
<Footer navigationItems={data.prismic.allNavigations.edges[0].node} />
{isDev amp;amp; <Devtools />}
</div>
</CSSTransition>
</TransitionGroup>
</AppContext>
</>
)}
/>
);
};