3

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.

TheWeeezel
  • 331
  • 1
  • 4
  • 16

2 Answers2

2

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.

TheWeeezel
  • 331
  • 1
  • 4
  • 16
  • You say it is not working then immediately follow with saying you figured out a working answer. Which is it? – Casey Mar 15 '20 at 17:48
  • 1
    So its the Solution. The Div you add to the Index.html. You can check how it looks and feels on spoortracking.ch – TheWeeezel Mar 16 '20 at 07:52
0

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.

Zakir Sajib
  • 293
  • 2
  • 7
  • 16
  • also working nice i belive. but i stick with my solution works like a charm – TheWeeezel Jun 17 '20 at 11:49
  • @TheWeeezel, I am getting error using your solution: ReferenceError: exports is not defined on exports.onInitialClientRender. Are you using old version of Gatsby? I am using the 2.23.3. But when I use this way it works: ```export const onInitialClientRender = () => { setTimeout(function() { document.getElementById("___loader").style.display = "none" }, 200) }``` – Zakir Sajib Jun 18 '20 at 07:30
  • yes this now leads to an error. ive updated it accordingly. thanks for letting me know. – TheWeeezel Jun 18 '20 at 08:30