skip to Main Content

I copied a simple full page preloader via jQuery on my Gatsby app. The preloader works however instead of disappearing after 4 seconds its loading forever on production but on local it works just fine.

Here’s my Preloader component:

import React from 'react'

 const Preloader = () => {
    return (
        <div id="loader-wrapper">
        <div id="loader"></div>
        <div className="loader-section section-left"></div>
        <div className="loader-section section-right"></div>
    </div>
    )
}

export default Preloader

And then on my script.js (which was included inside <Helmet>):

jQuery.noConflict();
    
(function ($) {

    var windows = $(window);

    /* preloader */
    setTimeout(function(){
        $('body').addClass('loaded');
        $('h1').css('color','#222222');
    }, 400);
    
   
})(jQuery);

I added this preloader on my index.js:

return (
     <>
    <Preloader />
    <SEO />
    <div className="main-container">
      <Layout>
      <Header />
      <Testimonials title="Testimonials" />
      <Blogs title="Latest Blogs" blogs={blogs} showAllBlogLinks/>
      <Map />
      </Layout>
    </div> 
    </>
  )
}

Any idea what’s causing this and how to fix it?

2

Answers


  1. Have you tried adding your code inside gatsby-browser.js instead something like this?

    export const onInitialClientRender = () => {
        setTimeout(function() {
            document.querySelector("body").classList.add("loaded");
            document.querySelector("h1").style = "color: #222222";
        }, 400)
    }
    
    Login or Signup to reply.
  2. I’ve faced a similar issue a few months ago and the fix depends strictly on the implementation and your code. Basically, what is happening is that React’s doesn’t understand that he needs to rehydrate some components because pointing to some global objects as window or document of the DOM, outside the React’s ecosystem (without using states) may block that rehydration, in your cause, because of jQuery.

    All the possible solutions that bypass this issue will be patches (like trying to add the cache). The ideal solution would avoid using jQuery, which points directly to the DOM, with React, which manipulates the virtual DOM (vDOM). This line is breaking your rehydration:

    var windows = $(window);
    

    The most weird thing here is that on local upon running npx gatsby
    develop it shows all of my data. Really weird

    It’s not, again: gatsby develop occurs in the browser, where there’s a window object, on the other hand, gatsby build occurs in the server (Node) where there’s no window or other global objects because they are not even created.

    In addition, you can easily transform this snippet:

    setTimeout(function() {
        document.querySelector("body").classList.add("loaded");
        document.querySelector("h1").style = "color: #222222";
    }, 400)
    

    Into a React-based one, using hooks:

       const [loader, setLoader]=useState(true);
    
       useEffect(()=>{
        setTimeout(()=> {
           setLoader(false)
        }, 400)
       }, [])
    
    return (
         <>
         <SEO />
        {loader 
          ? <Preloader />
          : <div className="main-container">
             <Layout>
                <Header />
                <Testimonials title="Testimonials" />
                <Blogs title="Latest Blogs" blogs={blogs} showAllBlogLinks/>
                <Map />
             </Layout>
           </div> 
         }
        </>
      )
    }
    

    As a resume. You are setting initially the loader as true (useState(true)), and once the DOM tree is loaded (useEffect with empty deps, []), you trigger your setTimeout function, which toggles the value of loader from true to false. This will choose what component render using a ternary condition at: loader ? <Preloader/> : <OtherComponents />

    If you need to use the window size object, you can use a React-based approach, like the one provided here.

    Is there a way to improve this and make the preloader loads everytime
    you visit another page?

    Yes, the idea is to move that loader logic to the <Layout> component, since all the pages will share it:

    const Layout = ({ children }) => {
           const [loader, setLoader]=useState(true);
        
           useEffect(()=>{
            setTimeout(()=> {
               setLoader(false)
            }, 400)
           }, [])
    
      return  <section className={`site__wrapper`}>
        <Header />
        <main>{loader ? <Preloader/> :  children}</main>
      </section>;
    };
    

    I don’t know how your <Layout> component looks like but the idea is to add something similar. Since your <Layout> is shared across your site, each re-rendering of the component will rehydrate this component, forcing the initial loader state as true and changing it to false at 400ms, rendering the children, which is everything wrapped inside the <Layout> component on other pages.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search