Static websites are my favorite thing in the web development industry right now, and I don't imagine they'll ever give that position up. They're incredibly fast, secure, and they're just awesome!
Recently, I had to hook up a Gatsby site to a serverless database and authentication service. I needed to have a way to manage the state of the app, such as user info, whether or not someone is signed in, and other information.
I couldn't use Redux, because there's not a main root element with static site generators.
I ended up using Gatsby's wrapRootElement
function that allowed me to effectively share state throughout every component on the site.
Here's how to effectively manage state in Gatsby.
In order to use our state throughout the site, the first thing we have to do is set up a state provider, using React Context.
Create a new folder at src/providers
called StateProvider.js
.
Inside this file, we'll use React.createContext()
to create our state, and create a functional component called StateProvider
that will provide our entire app with access to the context.
import React, { useState } from 'react';
export const StateContext = React.createContext();
export const StateProvider = ({ children }) => {
const [message, setMessage] = useState('A message!');
return (
<StateContext.Provider
value={{
message,
setMessage
}}
>
{children}
</StateContext.Provider>
);
};
Here's what we're doing.
First, we create a new context, called StateContext, and export it so it can be imported by other .js
files.
export const StateContext = React.createContext();
Next, we define our functional component, called StateProvider
.
export const StateProvider = ({ children }) => {
const [message, setMessage] = useState('A message!');
return (
...
);
};
Inside of this component, we're creating the state item that we want to change and access throughout the app. In this case, it's a message that we can display and set.
const [message, setMessage] = useState('A message!');
Finally, we need to return the StateContext.Provider
to the rest of the site, and wrap it around the children
prop.
return (
<StateContext.Provider
value={{
message,
setMessage
}}
>
{children}
</StateContext.Provider>
)
You'll notice we're passing a value
prop to the provider. This is necessary to give the rest of the site access to these values. If you don't pass these value here, they won't be accessible by the rest of the site, even if you defined them above using useState()
;
After we've got our StateProvider
set up, we need to wrap our site with it.
One way to do this would be to go into each page and wrap it with <StateProvider></StateProvider>
.
A second way of doing this is to just wrap the entire site with it using Gatsby's wrapRootElement
. This is the better option, because you only use the provider once, and the whole site receives the state.
At the root of your project, open the gatsby-browser.js
file. This is the file that Gatsby runs when the page first loads.
Inside this file, we're going to import our StateProvider
and call the wrapRootElement
function.
import React from 'react';
import { StateProvider } from './src/providers/StateProvider';
export const wrapRootElement = ({ element }) => {
return <StateProvider>{element}</StateProvider>;
};
We need to do to main things here: first, we need to access the element
prop, which Gatsby passes to the wrapRootElement
function when the page first loads.
The second and final thing we need to do is wrap the element
prop (which represents the entire site) with the state provider and return it.
return <StateProvider>{element}</StateProvider>;
Great! Now that we've got our state provider set up and wrapping our site, we can use it wherever we want.
For demo purposes, we'll be creating a simple messaging component that will display the current message, and allow you to set a new message as well.
In your src/pages
directory, create a new file called message.js
.
Inside this new file, we'll define our new page, and use our StateContext
that we created earlier.
import React, { useContext } from "react";
import { StateContext } from "../providers/StateProvider";
import "../components/style.css";
const Message = () => {
const { message, setMessage } = useContext(StateContext);
return (
<div className="wrapper">
<h1>Current message</h1>
<p>{message}</p>
<label htmlFor="set-message">Set new message</label>
<input type="text" id="set-message" placeholder="Message..." />
<button
onClick={() => setMessage(document.getElementById("set-message").value)}
>
Set
</button>
</div>
);
};
export default Message;
Basically, we import StateContext
from our StateProvider. We then grab the message
and the setMessage
function from the context, and use those to display and update the message!
So, I hope you've learned something new in this post! This post only covers the very basics of state management in Gatsby, but the idea is very simple: use a global state object so React can update everything that needs to be updated when that state changes.
Thanks for reading! If you liked this article, you can check me out on Twitter, or on my website!
p.s For those of you wondering, the official reading time for this article is 4 minutes and 40 seconds 😉⏳