Use an Async function in Gatsby wrapRootElement

So, I spent most of this afternoon figuring this out - finally realising it's actually quite simple. Cue facepalm.
My usecase: I'm using Apollo inside of Gatsby, for an eccommerce app. I will be sending mutations to the Shopify Storefront API with it, and as I was using Apollo I decided to use its built in local state to store customers carts in-between sessions. apollo-cache-persist
to the rescue!
But I had an isue - the cache persistor returns a promise while it is restoring the cache. So I needed to create an async function, which would be called when the app loaded. Cue code:
Note: No doubt this could be refactored to use React hooks - I.E
useState
anduseEffect
.
apollo.js
import React, { Component } from "react"
import { ApolloClient, InMemoryCache, HttpLink } from "apollo-boost"
import fetch from "isomorphic-fetch"
import { ApolloProvider } from "react-apollo-hooks" // Or just react-apollo
import { CachePersistor } from "@graphqlheroes/apollo-cache-persist" // or apollo-cache-persist
// Apollo Client Setup
const shopify = new HttpLink({
uri: `https://${SHOPIFY_URL}.myshopify.com/api/graphql`,
headers: {
"X-Shopify-Storefront-Access-Token": SHOPIFY_TOKEN
},
fetch
})
const cache = new InMemoryCache()
const persistor = new CachePersistor({
cache,
storage: localForage // Can be replaced with window.localStorage etc,
maxSize: false,
debug: process.env.NODE_ENV !== "production"
})
const client = new ApolloClient({
cache,
connectToDevTools: process.env.NODE_ENV !== "production",
link: shopify,
typeDefs,
resolvers
})
// Here comes the good bit...
class Apollo extends Component {
state = {
client: null,
loaded: false
}
async componentDidMount() {
try {
cache.writeData({
data: initialState
})
// Restore cache - I also use persistor in auth callbacks, i.e. persistor.purge() when a user logs out
await persistor.restore()
} catch (error) {
console.error("Error restoring Apollo cache", error)
}
this.setState({
client: apollo,
loaded: true
})
}
render() {
const { client, loaded } = this.state
const { children } = this.props
if (!loaded) return <div>Loading...</div>
return <ApolloProvider client={client}>{children}</ApolloProvider>
}
}
export default Apollo
gatsby-browser.js
,gatsby-ssr.js
import React from "react"
import ApolloProvider from 'path/to/apollo.js'
export const wrapRootElement = ({ element }) => (
<ApolloProvider>{element}</ApolloProvider>
)
I found that you really can't use async directly in wrapRootElement
. For example export const wrapRootElement = async ({ element })
throws an error Objects are not valid as a React child (found: [object Promise]).