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 😉⏳