Search code examples
reactjsreact-hooksgatsbyuse-effectuse-state

Change React State if Navigating to New Path


I am trying to implement react-transition-group, and need to be able to change a state of fadeEffectVisible from false to true assuming that the path that a user is navigating to is not the same as the current path. In other words, it should work if a user navigates from page-1 to page-2 but not if a user is on page-1 and clicks a link to page-1. I'm using hooks and functional components, and this is my AppLayout component as it stands right now.

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

Am I even close to being on the right path here? Thanks!


Solution

  • Figured this out, thanks to a great deal of help from Ferran's answer, but I wanted to share my full component now that I've got it working. Specifically, this is how I got it set up to correctly update my page state, and check the previous url against the target.

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