【问题标题】:Change React State if Navigating to New Path如果导航到新路径,则更改 React 状态
【发布时间】:2021-02-09 05:59:19
【问题描述】:

我正在尝试实现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>
        </>
      )}
    />
  );
};

我是否已经接近正确的道路了?谢谢!

【问题讨论】:

  • path !== path 将始终为假。您是否需要将当前路径值与之前的路径值进行比较? How to get the previous props or state? 还是您需要针对当前路径测试目标路径?
  • 我需要针对当前路径测试目标路径。
  • 那么,像targetPath !== path 导航之前检查一下?如果已经在 that 路径上,您可以完全阻止导航吗?我认为我们需要更多的代码上下文。能否提供更完整的组件代码示例?
  • 问题更新了更多代码。感谢您的观看。
  • 我认为您可能很接近,但尚不清楚path 的定义位置和来源。如果pathprevPath 以及效果依赖项都是正确的,则更简洁的实现将是setfadeEffectVisible(prevPath !== path) int he effect。不过,这会将fadeEffectVisible 的值延迟一个渲染周期,我不确定这就是您所追求的。状态值似乎只是来自prevPathpath 的派生值,所以也许只是计算in 当前渲染周期,即in={prevPath !== path}

标签: reactjs react-hooks gatsby use-effect use-state


【解决方案1】:

多亏了 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>
        </>
      )}
    />
  );
};

【讨论】:

    【解决方案2】:

    我会使用另一种方法,使用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 扩展而来)仅在顶级组件(页面)中。可能pathname 属性并不完全符合您的需求,请查看以找出符合您要求的属性。

    参考资料:

    【讨论】:

    • 谢谢,太好了。不过我遇到了一个小问题,那就是const prevPage = usePrevious(page) 永远不会更新到最近的上一页;它只返回第一个state
    • 你可以多玩一点钩子(不违反钩子的规则)。尝试创建一个useEffect,并将该位置作为依赖项来触发您的usePrevious
    猜你喜欢
    • 2023-01-18
    • 2021-07-21
    • 2018-01-24
    • 2021-04-21
    • 2021-08-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-26
    • 1970-01-01
    相关资源
    最近更新 更多