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
Have you tried adding your code inside
gatsby-browser.js
instead something like this?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
ordocument
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:
It’s not, again:
gatsby develop
occurs in the browser, where there’s awindow
object, on the other hand,gatsby build
occurs in the server (Node) where there’s nowindow
or other global objects because they are not even created.In addition, you can easily transform this snippet:
Into a React-based one, using hooks:
As a resume. You are setting initially the loader as true (
useState(true)
), and once the DOM tree is loaded (useEffect
with emptydeps
,[]
), you trigger yoursetTimeout
function, which toggles the value ofloader
fromtrue
tofalse
. 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.Yes, the idea is to move that loader logic to the
<Layout>
component, since all the pages will share it: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 astrue
and changing it tofalse
at 400ms, rendering thechildren
, which is everything wrapped inside the<Layout>
component on other pages.