0

I've been trying to diagnose why I've got useEffect running multiple times when I click a <Link> component, even with empty dependencies. I found a good way to figure out where my problem lies, using the solution in this answer.

Essentially I'm adding a useEffect that console logs components went they re-render, and I went all the way up the tree till I found the highest level component that doesn't re-render.

useEffect(()=>{
  console.log('this component is rendered');
},[]);

I'm using gatsby-plugin-layout, and my AppLayout component is the component where the re-render does not occur, meaning that the problem lies within my page templates, as defined in gatsby-source-prismic-graphql, which are the next things down the tree where the console logs the page change when the <Link> is clicked. All this to say, I'm not entirely sure where the problem here lies, or if there is something within my AppLayout or gatsby-config.js that is throwing it off and making this behavior occur. How can I fix it so my pages aren't constantly re-rendering? Included below are my AppLayout and gatsby-config.js.

Thanks in advance.

gatsby-config.js

/* eslint-disable @typescript-eslint/camelcase */
const path = require('path');

require('dotenv').config({
  path: `.env.${process.env.NODE_ENV}`,
});

const site = require('./config/site');

const pathPrefix = site.pathPrefix === '/' ? '' : site.pathPrefix;

module.exports = {
  pathPrefix: site.pathPrefix,
  siteMetadata: {
    siteUrl: site.url + pathPrefix,
    pathPrefix,
    title: site.title,
    titleAlt: site.titleAlt,
    titleTemplate: site.titleTemplate,
    description: site.description,
    banner: site.logo,
    headline: site.headline,
    siteLanguage: site.siteLanguage,
    ogLanguage: site.ogLanguage,
    author: site.author,
    twitter: site.twitter,
    facebook: site.facebook,
  },
  plugins: [
    'gatsby-plugin-react-helmet-async',
    'gatsby-plugin-typescript',
    'gatsby-plugin-sharp',
    'gatsby-plugin-sitemap',
    'gatsby-transformer-sharp',
    {
      resolve: 'gatsby-source-filesystem',
      options: {
        name: 'images',
        path: path.join(__dirname, `src`, `assets`, `images`),
      },
    },
    {
      resolve: 'gatsby-plugin-manifest',
      options: {
        name: 'one-day-doors-and-closets',
        short_name: 'starter',
        start_url: '/',
        background_color: '#663399',
        theme_color: '#663399',
        display: 'minimal-ui',
        icon: 'src/assets/images/icon.png',
      },
    },
    {
      resolve: '@sentry/gatsby',
      options: {
        dsn: process.env.SENTRY_DSN,
        enabled: (() => ['production'].indexOf(process.env.NODE_ENV) !== -1)(),
      },
    },
    {
      resolve: 'gatsby-plugin-react-svg',
      options: {
        rule: {
          include: path.resolve(__dirname, 'src/assets/svg'),
        },
      },
    },
    {
      resolve: 'gatsby-source-prismic-graphql',
      options: {
        repositoryName: `${process.env.PRISMIC_REPOSITORY_NAME}`,
        defaultLang: 'en-us',
        path: '/preview',
        previews: false,
        accessToken: `${process.env.PRISMIC_ACCESS_TOKEN}`,
        pages: [
          {
            type: 'Homepage',
            match: '/',
            component: require.resolve('./src/pages/index.tsx'),
          },
          {
            type: 'Landing',
            match: '/:uid',
            component: require.resolve('./src/templates/landing.tsx'),
          },
          {
            type: 'Legal',
            match: '/:uid',
            component: require.resolve('./src/templates/legal.tsx'),
          },
          {
            type: 'Locator',
            match: '/:uid',
            component: require.resolve('./src/pages/locations.tsx'),
          },
          {
            type: 'Product',
            match: '/products/:uid',
            component: require.resolve('./src/templates/product.tsx'),
          },
        ],
      },
    },
    {
      resolve: 'gatsby-plugin-layout',
      options: {
        component: require.resolve('./src/components/app-layout/AppLayout.tsx'),
      },
    },
  ],
};

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';
export const timeout = 250;

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

  useEffect(
    () => () => {
      if (pathname !== prevPage) {
        setFadeEffectVisible(true);
        setPage(page);
      }
    },
    [pathname],
  );

  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>
        </>
      )}
    />
  );
};
diedu
  • 19,277
  • 4
  • 32
  • 49
Jesse Winton
  • 568
  • 10
  • 33
  • 2
    It's hard to tell why just from the info you've shared (even though already plenty.) Just a guess, have you tried breaking everything under `` into its own component & wrap it in a React.memo? When context change, it may cause everything in it to re-render. For better debugging, you can try recording your site's performance with React Devtool, click Link once & check why things are re-rendered. – Derek Nguyen Nov 06 '20 at 17:39
  • Have you tried removing the animations? – diedu Nov 07 '20 at 04:46
  • I have. I think Derek is on the right track, seems to be something related to my `contexts`. Working on debugging that. – Jesse Winton Nov 08 '20 at 03:11
  • 1
    Seems as though it had something to do with `gatsby-source-prismic-graphql`. Migrating to `gatsby-source-prismic`, which is recommended by Prismic now, seems to have fixed it. – Jesse Winton Nov 09 '20 at 18:35

1 Answers1

0

Hard to get a sense of what is going on, however, I see your useEffect code

YOUR CODE

useEffect(() => () => {
        if (pathname !== prevPage) {
            setFadeEffectVisible(true);
            setPage(page);
        }
    },
        [pathname],
    );

REFACTOR TO THIS

useEffect(() => {
        if (pathname !== prevPage) {
            setFadeEffectVisible(true);
            setPage(page);
        }
    },
        [pathname],
    );
Grey
  • 489
  • 3
  • 7