Show overlay only once on page entrance (not route change). HowTo?

All we need is an easy explanation of the problem, so here it is.

So I try to have an overlay over my gatsby site for about a split second to let it load the correct breakpoint in the back. How can I achieve this?

So I set an Interval and some State to load the Overlay and make it disappear after an interval. But the problem after each page reload the overlay appears again. I have no state management like redux and I need the Overlay to only appear after every refresh or entrance from external links but not clicking on internal links.

I’ve tried useEffect with only render on the mount but this does not do the trick as internal link changes will trigger a remount

I figured out that I could use the Browser API onInitialClientRender. But I don’t understand how a value generated in there can be accessed outside of the gatsby-browser.js. In general, I have issues understanding how to use these APIs.

customHooks.js

export function useInterval(callback, delay) {
  const savedCallback = useRef()

  // Remember the latest callback.
  useEffect(() => {
    savedCallback.current = callback
  }, [callback])

  // Set up the interval.
  useEffect(() => {
    function tick() {
      savedCallback.current()
    }
    if (delay !== null) {
      let id = setInterval(tick, delay)
      return () => clearInterval(id)
    }
  }, [delay])
}

Layout.js

let [loadingIndicator, setLoadingIndicator] = useState(true)
const delay = 500

useInterval(() => {
  setLoadingIndicator(false)
}, delay)

So the expected result would be a way to tell on the initial client render to set a state which tells the Overlay to be rendered and after this one render the state should change to disable the render of the overlay.

How to solve :

I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.

Method 1

Solution:

Using gatsby’s ability to change the static index.html file. [Customize Index.html in Gatsby][1]

Adding a custom div to the generated html.js

html.js:

   <div
      key={`loader`}
      id="___loader"
      style={{
        alignItems: "center",
        backgroundColor: "#F2F2F2",
        display: "flex",
        justifyContent: "center",
        position: "absolute",
        left: 0,
        top: 0,
        right: 0,
        bottom: 0,
        zIndex: 1,
      }}
   >
      <LoaderSVG width="50%" />
   </div>

and in gatsby-browser.js:

gatsby-browser.js:

export const onInitialClientRender = () => {
  document.getElementById("___gatsby").style.display = "block"
  setTimeout(function() {
    document.getElementById("___loader").style.display = "none"
  }, 200)
}

The next step is to check whether all the data is loaded and add a dynamic value instead of the timeout.

Method 2

I did something like this.

I copied the default html from .cache directory and pasted into src directory.

I used following command:

cp .cache/default-html.js src/html.js

in the html.js file, I noticed following codes:

import React from "react"
import PropTypes from "prop-types"

export default function HTML(props) {
  return (
    <html {...props.htmlAttributes}>
      <head>
        <meta charSet="utf-8" />
        <meta httpEquiv="x-ua-compatible" content="ie=edge" />
        <meta
          name="viewport"
          content="width=device-width, initial-scale=1, shrink-to-fit=no"
        />
        {props.headComponents}
      </head>
      <body {...props.bodyAttributes}>
        {props.preBodyComponents}
        <div
          key={`body`}
          id="___gatsby"
          dangerouslySetInnerHTML={{ __html: props.body }}
        />
        {props.postBodyComponents}
      </body>
    </html>
  )
}

HTML.propTypes = {
  htmlAttributes: PropTypes.object,
  headComponents: PropTypes.array,
  bodyAttributes: PropTypes.object,
  preBodyComponents: PropTypes.array,
  body: PropTypes.string,
  postBodyComponents: PropTypes.array,
}

I put SVG loader which is an image and few lines of CSS:

So complete code like this:

import React from "react"
import PropTypes from "prop-types"
import LoaderSVG from './img/loader.svg'

export default function HTML(props) {
  return (
    <html {...props.htmlAttributes}>
      <head>
        <meta charSet="utf-8" />
        <meta httpEquiv="x-ua-compatible" content="ie=edge" />
        <meta
          name="viewport"
          content="width=device-width, initial-scale=1, shrink-to-fit=no"
        />
        {props.headComponents}
      </head>
      <body {...props.bodyAttributes}>
        {props.preBodyComponents}
        <div
              key={`loader`}
              id="___loader"
              style={{
                alignItems: "center",
                backgroundColor: "#F2F2F2",
                display: "flex",
                justifyContent: "center",
                position: "absolute",
                left: 0,
                top: 0,
                right: 0,
                bottom: 0,
                zIndex: 1,
              }}
           >
              <img src={LoaderSVG} alt="" width="150"/>
        </div>
        <div
          key={`body`}
          id="___gatsby"
          dangerouslySetInnerHTML={{ __html: props.body }}
        />
        {props.postBodyComponents}
        <script
          dangerouslySetInnerHTML={{
            __html: `
                setTimeout(function() {
                    document.getElementById("___loader").style.display = "none"
                }, 200)
            `,
          }}
        />
      </body>
    </html>
  )
}

HTML.propTypes = {
  htmlAttributes: PropTypes.object,
  headComponents: PropTypes.array,
  bodyAttributes: PropTypes.object,
  preBodyComponents: PropTypes.array,
  body: PropTypes.string,
  postBodyComponents: PropTypes.array,
}

And then restart Gatsby and it works.

Note: Use and implement method 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply