Use an Async function in Gatsby wrapRootElement

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 and useEffect.

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]).

Address

Address?
Not happening.
I get enough email spam as it is ¯\_(ツ)_/¯