React Suspense with GraphQL

Warning, the React docs say that Suspense does not yet support data loading, so in the future there may be breaking changes & better options available.

TLDR: check out graphql-suspense to get up & running with GraphQL & suspense.


If you look at the React documentation as of this writing, you’ll see this:

While this is not supported today, in the future we plan to let Suspense handle more scenarios such as data fetching. You can read about this in our roadmap.

While this may be true, it’s already possible to start using Suspense with data fetching with the current implementation.

The mission: Use suspense to fetch data from a GraphQL API.

I found myself experimenting with Suspense & GraphQL a few months ago. My naive implementation seemed to work well enough for some basic operations.

To see where the createFetcher function came from, check out this tweet:
// function that will throw promise for Suspense to catch
const createFetcher = fetcher => {
let cache = {};
return {
read: (...args) => {
if (cache[args] === undefined) {
throw fetcher(args).then(v => (cache[args] = v));
} else {
return cache[args];
}
}
}
}
// calling createFetcher
const myResource = createFetcher(async() => {
const data = await API.graphql(graphqlOperation(query))
return data.listTodos.items
})
function Data() {
const todos = myResource.read()
return todos.map((t, i) => <p>Todo {i}</p>)
}
<Suspense fallback={<div>loading...</div>}>
<Data />
</Suspense>
Here’s how suspending works: 
1. In the render method, read a value from the cache. 
2. If the value is already cached, the render continues like normal 
3. If the value is not already cached, the cache *throws a promise*
4. When the promise resolves, React retries where it left off
 — Andrew Clark

This week I attempted to write a function that would abstract much of this code into something nice & reusable & provide better caching (allowing caching of non strings). In my research, I found a nice utility that allows developers to use Suspense to fetch data.

To understand how data fetching with suspense works, let’s first take a look at a *really* good explanation by Charles Stover in his article React Suspense with the Fetch API.

Using a similar implementation that Charles came up with, I was able to get a similar abstraction working that is compatible with a couple of different GraphQL clients. Here’s how it works:

import gqlsuspense from 'graphql-suspense'
const client = new ApolloClient({
uri: "<SOMEURI>"
})
class Data extends React.Component {
render() {
const data = gqlsuspense(client.query, { query: listTodos })
return data.listTodos.map((t, i) => <p key={i}>{t.name}</p>)
}
}
const App = () => (
<Suspense fallback={<div>loading ...</div>}>
<Data />
</Suspense>
)

That’s it! Using gqlsuspense you can now use Suspense to fetch data.

This makes sense to use in cases when you’re fetching data, but I’ve also tried it when making mutations (though I’m not sure how useful that is) & it works. I haven’t been able to figure out a good abstraction with state management yet, maybe someone smarter or with more time can add to this conversation with some ideas.

I’m also interested in how subscriptions will work in this paradigm 🤔…

I’m certain that down the road there will be many more & better solutions than this, for instance I’m assuming that GraphQL clients will have this functionality built-in down the road & the newer versions of Suspense will have a nice built-in API, but if you’re interested in trying this out, give it a shot.

If you have any ideas / PRs, feel free to send them my way.