SWR keys and revalidation
Over the last half year I've worked with SWR, a "React Hooks library for data fetching" by the same group of people behind Next.js and Vercel. It's a neat library that I like specifically for it's link to an actual RFC, RFC 5861:
The stale-while-revalidate HTTP Cache-Control extension allows a cache to immediately return a stale response while it revalidates it in the background, thereby hiding latency (both in the network and on the server) from clients.
A basic use case for using SWR in a component might look like this (much less basic than the one from the docs, though):
;
;
;
A couple things to clarify to avoid confusion:
fetcher
is a wrapper around fetch that we use for client-side requests, we'll ignore this.getLovelyNameByIdQuery
is a generic GraphQL query wrapped with graphql-tag, we'll also ignore this.
The key point I want to discuss in this note is the way we define keys to use in our SWR cache:
lovelyId ? `lovely-name-` : null,
This understated line is arguably the most important. To understand why, we have to introduce one other concept from the SWR library: revalidation.
Revalidation
Revalidation is the process of checking whether the data displayed in our client (and stored in our cache) needs to update to match what is stored in our database. Aside from the special cases where the SWR library will revalidate for us, namely on window focus (revalidateOnFocus
) and when a network connection is dropped and regained (revalidateOnReconnect
), there are two ways we can tell SWR to revalidate our data.
Bound mutate
The bound mutate
function is returned from the useSWR
hook wherever you implement it. In our LovelyNameComponent
, that would look like this:
This is great if we want to be able to revalidate in this component or another component that is nearby in the DOM, but not practical if we want to revalidate from some other faraway part of our app. Enter the second option to revalidate: the global mutate
:
Global mutate
SWR's docs say this about the global mutate function:
You can broadcast a revalidation message globally to all SWRs with the same key by calling
mutate(key)
.
Continuing our scenario, that would look like this:
;
This is a great solution to avoid passing around too many bound mutate
functions, but it requires us to know two things to make it work:
- The SWR cache key name used in
LovelyNameComponent
- The value of the parameter interpolated in the cache key name (
lovelyId
)
While not a problem in small apps, medium to large apps with hundreds or thousands of different requests present a challenge.
Introducing an SWR key registry
A good solution to this challenge should help achieve the following:
- Avoid unnecessary duplication of requests
- Leverage the cache in all places where the cache can be leveraged
- Easily revalidate requests via the global mutate function
- Reduce bugs that occur when we change SWR keys
To do this, we introduced the concept of an SWR registry via a TypeScript string enum:
We then use the registry like this:
lovelyId ? ` -` : null,
However, only solves half the problem. We also need to know the parameters interpolated with the key!
Exporting key getters
Instead of exporting and using the SWRCacheKey
enum directly, we can export getters that allow us to define a signature for interpolated parameters and associate it with the cache key.
;
We would then use it like this:
lovelyId ? lovelyId : null,
And if we're using a decent text editor like VSCode, the editor will show us what a list of possible getters and the associated signature for each via IntelliSense as we write out our SWR code.
System scaling
For completeness' sake, let's consider one last scenario to imagine how this system would scale. In this case, we introduce another component that is similar but slightly different to the one in LovelyNameComponent
. Instead of getting a single lovely name, our query returns an array of lovely names. Our system can handle this readily, using the same SWRCacheKey
property.
;
And there we have it -- thanks for reading, and happy revalidating!
Thanks for reading! Go home for more notes.