Gatsby’s Global State Management With React’s Context

Yingqi Chen
The Startup
Published in
4 min readAug 9, 2020

Gatsby is a framework based on React to generate a website faster. So as you can imagine, a lot of problems can be handled in a “React” way. In this article, I am going to talk about how to deal with Gatsby’s state management with React’s context.

What I usually do in React

Usually, if I want to grab a user object on every page in my website, in React, I will use componentDidMount to do that in my App.js like so:

//App.jsclass App extends React.Component {
componentDidMount() {
// do something to grab the User
fetchUser()// will grab the user and do something with that user page
}
}

Since it is a single page application, all of other components are children to App, so we know that fetchUser is fired every single time the website is rendered, when App component is rendered.

But the problem is, there’s no such thing in Gatsby as the parent to all of other components. So we have to think about other ways to make it happen.

Possible options to deal with states in Gatsby

There are a lot of ways to handle states in Gatsby. I prefer to use React’s Context to deal with it this time for many reasons, but I would still introduce what else you can use so you can use the one that fits your situation.

Handle states separately

Of course, you can always handle states separately in their own component. But it is going to cause a problem when you need to pass different props and state among various components frequently. That’s redundant and error-prone.

Redux

For people that use React, Redux might be familiar when coming to dealing with states. It is a cool tool to manage multiple states in your app that are communicated and mutated frequently among different components.

However, I don’t want to use this on my Gatsby app because I am not handling too much global data. So I personally don’t want to set up everything for this library just to handle one user state. (In Gatsby, you need to install a plugin to use Redux).

React Context

I like Redux’s solution where you put all the global states in ONE place(“store” in Redux) and pull out the one you need when needed while I don’t have to do too much set up. If you are in the same situation, use React Context!

React Context?

React context is built for solving this type of problem. Just like what is illustrated in the documentation:

In a typical React application, data is passed top-down (parent to child) via props, but this can be cumbersome for certain types of props (e.g. locale preference, UI theme) that are required by many components within an application. Context provides a way to share values like these between components without having to explicitly pass a prop through every level of the tree.

How to use it, then?

Try to think of context as a “store”, which stores all states you need to use among all different components. And when you need to use it, you just need to “go to the store”, and get the stuff you want.

Ok, so you see there are two steps: store the data and get the data. I will explain to you how it is implemented in coding language.

Where’s the store?

A very simple truth: the store has to be somewhere that is reachable. If you are in Europe, it is not likely that you would go to a store in America, right? I mean, even you would for whatever reason, it is definitely not considered as “convenient”. So that is not what we want.

So speaking of “reachable”, we can use Context.Provider to provide the data like this:

import React, { useState, createContext } from "react"export const UserStateContext = createContext(null)const Layout = ({ children }) => {
const [user, setUser] = useState()
console.log(user)
...// do something to set or fetch the userreturn (
<UserStateContext.Provider value={user}>
<SEO />
<Navbar />
<main>{children}</main>
</UserStateContext.Provider>
)
}
export default Layout

As you can see, first I create a context, and then I wrap up the components, where I want to use theuser object, with the context I just create, which is UserStateContext here. The user object is passed as the value prop at the same time.

Since I need a component that can be parent to all the components I need to use, I will use Layout component since I will use that on every page.

How to access the data?

Now I store data. How can I use that? The answer is Context.Consumer . Just like what I do with Provider, I should wrap up the EXACT component that I want to use the value, with Context.Consumer like so:

const Dashboard = props => {       return (
<UserStateContext.Consumer>
{user => {
return user? <LoggedIn user={user} /> : <NotLoggedIn propertiesPassed={propertiesPassed}/>
}}
</UserStateContext.Consumer>

)
}

Once it is wrapped by <UserStateContext.Consumer>, I can use the value prop that is “stored” in my context. You can see that here:

{user => {
return user? <LoggedIn user={user} /> : <NotLoggedIn propertiesPassed={propertiesPassed}/>
}}

I name the prop as user when I extract that, and then control which component I should render using its value. But also, you can name it whatever you want!

Thanks for reading!

--

--

Yingqi Chen
The Startup

Software engineer and a blockchain noob. Excited about the new world!!LinkedIn:https://www.linkedin.com/in/yingqi-chen/