Converting a React Site to Gatsby (part 2)
Using dynamic data at runtime
Obviously the maps themselves can't be generated ahead of time and have to remain dynamic. This presents us with an issue with the build process. The Gatsby build process is a node process on the server and hence does not have access to any browser objects and so this code needs to be hidden or else the build fails. This snippet is from our map component:
if (typeof window !== 'undefined') {
return (
<div>
{SEOtag}
{mapTag}
<Nav />
</div>
)
}
return SEOtag
What's happening here is that we can't access the window object during the the server-run build process and so we check to see if window is defined. If not we only render the SEOtag variable (contains header metadata via react-helmet library). At runtime, when the code is running in the browser the window object exists and the app 'hydrates' and the maps are rendered. When googlebot visits then the pre-rendered static page contains all required SEO information. We are told that Google can actually render React pages now; but ...this is by way of guarantee.
Accessing context to pass global data
In Gatsby we have to write a ContextProvider component in which our context objects are created. In order to wrap these around our app we need to tap into the hook wrapRootElement in both gatsby-browser.js for development and gatsby-node.js for production. For our project we have the following code GlobalContextProvider.js providing two React context objects.
import React, { useState } from 'react'
import PropTypes from 'prop-types'
export const NavContext = React.createContext()
export const GalleryContext = React.createContext()
const GlobalContextProvider = ({ children }) => {
const [nav, setNav] = useState({
currentPage: null,
previousPage: null,
})
const [galleryPhotos, setGalleryPhotos] = useState([])
return (
<NavContext.Provider value={[nav, setNav]}>
<GalleryContext.Provider value={[galleryPhotos, setGalleryPhotos]}>
{children}
</GalleryContext.Provider>
</NavContext.Provider>
)
}
export default GlobalContextProvider
GlobalContextProvider.propTypes = {
children: PropTypes.node.isRequired,
}
And for gatsby-browser.js:
import React from "react"
import GlobalContextProvider from './src/components/GlobalContextProvider'
export const wrapRootElement = ({ element }) => <GlobalContextProvider>{element}</GlobalContextProvider>
Review
The outcome of the conversion was staggering in terms of the performance of the site. Page downloads were a fraction of what they were. Gatsby also comes with things like lazy image loading for free. The only downside is the length of the build process. We currently have over 2,000 images on the site. The first build in which these assets are downloaded and rendered takes over an hour. Subsequently the build times are less but all the html pages are rebuilt which takes about 20 minutes. Gatsby are doing a lot of work on incremental builds so hopefully we'll see some improvement there soon.