Gatsby’s Global State Management With React’s Context
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!