Handle global state in a Gatsby app without using Redux

Last week I post a blog about fetch data from the database/API with useEffect and useContext. That is going to enable the component to fetch some data every time it is rendered. But what about those global states that would be shared by different components scattered all over the application?

What I usually do in a React app

For example, when we have a user component and we want to pass it to Dashboard and NavBar, what can we do? Since Gatsby is a React framework, I try to compare how I implement it in React. I remember I will use Redux. Just in case you don’t know too much about Redux, I will explain some more.

Since usually React is used to produce a single page application, there is an index.js in React which represents that single page, in other words, the whole app. The architecture usually looks like:

index.js
-AppComponent.js
-componentA.js
-componentB.js
...

Therefore, Redux will wrap App component with something called store, which includes all states or dispatch methods and thus all components within the App component would have access to those states or methods .

What I can do in the Gatsby app

Modify the htmj.js

In the Gatsby app, there is no such thing called index.js by default. There is another thing called html.js which is usually hidden. You can usually unveil it with this command:

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

And now you will see it and insert HTML into header and footer or add some custom JavaScript(where you can grab the data).

But usually, I don’t use that because I think there’s a reason that it is hidden, and don’t want to by accident mess up the configuration. (Let me know if you know why it is hidden though 😆). Also, to insert some lengthy JS codes in a file that looks like HTML(even the suffix is js) is really ugly and doesn't make sense to me.

Set up a Layout Component

The second way is to manually set up a Layout component and grab all the data that I need there using useEffect and useContext just like what I illustrated in the last blog. Then a Layout component will probably look like this:

import React,{useState, createContext }  from "react"import Navbar from "./navBar"export const SomeContext = React.createContext(null);// don't forget to export, so that it can be used outside of this componentconst Layout = ({ children }) => {  

const [user, setUser] = useState(null)
useEffect(() => {
getUser = async() => {
let user = await fetchData()
setUser(user)
}
getUser();
, []
})
return (
<SomeContext.Provider value={{user}}>
<Navbar />
<main>{children}</main>
</SomeContext.Provider>
)
}
export default Layout

In this way, everything we wrap with Layout component will automatically become the children and would be able to use the user object that is provided through SomeContext.

Therefore, we can wrap up all of the components within this Layout component, just like so:

const AnyComponent = props => {   return <Layout>{any component content}</Layout>
}

Done! Now every time you want to use the user, you can use it like so:

import React, {useContext} from "react"
import {SomeContext} from "../layout"
const AnyComponent = props => {

const {user} = useContext(SomeContext)
return <Layout>{user.name}</Layout>
}

Handle Layout component if you are a lazy person

That’s said, even we can use the user object anytime we want with the context API, it is kind of annoying to have to wrap up all components with Layout component manually every time and import it on every file.

What if we can import Layout component somewhere and the application would automatically wrap up with it? That sounds awesome!

The trick is: use wrapPageElement.

Go to the root folder and put this on gatsby-ssr.js and gatsby-browser.js:

import Layout from './src/components/layout'export const wrapPageElement = ({element, props}) => {
return <Layout {...props}>{element}</Layout>
}

Now you don’t have to import Layoutcomponent all the times and it will be rendered automatically with other components! Bravo!

Note

Besides wrapPageElement, there is one called wrapRootElement. Their difference is illustrated in this article. For those who don’t want to read, here’s the straight answer I picked from there:

…yes, you can use wrapPageElement element for everything, but it’s better to use it only for providers that need the router props, or for page transition layouts, and use wrapRootElement for any other provider, like theme and global state providers.

Thanks for reading!

Web Developer | Ruby | Rails | SQL | Sinatra | React | Redux | HTML&CSS | JavaScript| MERN LinkedIn:https://www.linkedin.com/in/yingqi-chen/

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store