如果导航到新路径,则更改 React 状态

Change React State if Navigating to New Path

我正在尝试实现 react-transition-group,并且需要能够将 fadeEffectVisible 的状态从 false 更改为 true 假设用户的路径正在导航到与当前路径不同。换句话说,如果用户从 page-1 导航到 page-2,但如果用户在 page-1 上并单击 link 到 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>
        </>
      )}
    />
  );
};

我在这里走上正确的道路了吗?谢谢!

我会使用另一种方法使用 useRef 挂钩来存储上一个页面路径(存储在 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 是 Gatsby 公开的 prop(因为它从 React 的 @reach/router 扩展而来)仅在 top-level 组件(页面)中.可能 pathname 属性 不完全符合您的需求,检查一下以找出哪个 属性 符合您的要求。

参考文献:

由于 Ferran 的回答提供的大量帮助,我明白了这一点,但我想分享我的完整组件,因为我已经开始工作了。具体来说,这就是我如何设置它以正确更新我的 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 && <Devtools />}
                </div>
              </CSSTransition>
            </TransitionGroup>
          </AppContext>
        </>
      )}
    />
  );
};