Lawrence Eagles Senior full-stack developer, writer, and instructor.

SWR v1 is here: What’s new?

5 min read 1469

SWR V1 is Here

Introduction: What’s SWR?

SWR is a hooks library that provides React Hooks for remote data fetching. As the SWR docs state, the name “SWR” is derived from stale-while-revalidate, which is an HTTP cache invalidation strategy.

In a nutshell, the stale-while-revalidate strategy simply means serving or returning the stale (cached) data while you revalidate (fetch) the updated data and serve the updated data to the UI once it is returned. The revalidation of stale data occurs under the hood on user focus or network reconnect.

The result of this is that our application components get a stream of fresh data constantly, making our UI blazing fast, very reactive, and more responsive.

Another awesome feature we get out of the box that redounds to our application performance is deduplication. Deduplication is a technique to eliminate redundant data, and by implementing this technique, SWR does intelligent fetching.

What this is means is that when we render a component that reuses the useSWR hook multiple times, SWR would prevent multiple uses of the useSWR hook to revalidate data if the keys are the same, consequently eliminating unnecessary network requests because only one request would be made and stale data would be served to subsequent requests.

In this article, we’ll be looking at the awesome additions to SWR version 1, but if you are new to SWR, you can get started by reading our previous useSWR article.

New features in SWR v1

1. Smaller size and improved performance

SWR version 1 provides a significantly smaller library and improved performance features like tree shaking. According to the documentation, version 1 is significantly smaller — 41 percent smaller core or 24 percent smaller when gzipped.

The SWR package is tree-shakable because performance is one of the most important features of SWR.

Version 1 supports path imports as seen below:

import useSWR from 'swr'
import useSWRInfinite from 'swr/infinite'

With path imports, in cases where we are not using the useSWRInfinite hook, it would not be bundled in our application. Removing dead code makes our final bundle size significantly smaller.

We made a custom demo for .
No really. Click here to check it out.

By making the library more tree-shakable and lightweight, our application will have a smaller bundle, a leaner runtime, and a smaller node_modules directory.

2. Custom cache provider

By default, SWR uses a global cache. However, version 1 takes this further by providing the provider API. The provider API consists of a cache provider that is a map-like object that matches the following TypeScript definition:

interface Cache<Data> {
  get(key: string): Data | undefined
  set(key: string, value: Data): void
  delete(key: string): void
}

The cache provider is passed to the provider option of the SWRConfig as seen below:

import useSWR, { SWRConfig } from 'swr'

function App() {
  return (
    <SWRConfig value={{ provider: () => new Map() }}>
      <Blog/>
    </SWRConfig>
  )
}

The result of this is that our custom cache provider will be used by all the SWR hooks within SWRConfig. In other words, all the SWR hooks inside Blog will read and write from that map instance, but the SWR hooks that are not nested within the cache provider fall back to the default cache, which is an empty map. Because of this, cache providers should be placed higher in the component tree or outside render.

Cache providers are intended to adapt SWR to support more storage options, and they can be a JavaScript Map instance or other cache provider implementation depending on our use case.

SWR v1 provides experimental support for extending the current cache provider. Consequently, we can sync the cache provider with the local storage, reset the cache between test cases, and more with the extended cache provider.

Also, in version 1 we can get access to the current cache provider when inside a React component by using the useSWRConfig hook as seen below:

import { useSWRConfig } from 'swr'

const Post() {
  const { cache, ...extraConfig } = useSWRConfig()
  cache.get(key) // Get the current data for the specified key.
  cache.clear()  // Clear all the cache. SWR will revalidate upon re-render.
  // ...
}

N.B., writing to the cache directly might cause undefined behaviors of SWR. The recommended practice is to use SWR APIs such as mutation.

3. useSWRConfig()

While the SWRConfig context provides global configuration options for all SWR hooks, the new useSWRConfig() hook enables us to access these options when inside a React component.

Consider the code below:

import { useSWRConfig } from 'swr'

function MyComponent () {
  const { refreshInterval, mutate, ...restConfig } = useSWRConfig()
  // ... do something
}

From our example on caching, we saw that we can also access the cache using useSWRConfig.

4. Immutable mode

Out of the box, SWR supports automatic revalidation on the following events:

Revalidate on focus

This revalidation occurs when we refocus a page or switch between tabs. This helps to provide fresh data to our app. By default, this feature is turned on, but you can disable it via the revalidateOnFocus option.

Revalidate on interval

In many cases, our app’s data can become stale quickly because of many devices, multiple users, and multiple open tabs. This feature enables SWR to perform polling on interval, and this provides a way to synchronize to the latest state and update the data on the UI over time.

You can enable this by setting the refreshInterval option with the value in the fetcher function:

useSWR('/api/todos', fetcher, { refreshInterval: 2000 })

The above code simply means polling would only occur every 2000ms.

Also, SWR enables us to perform polling when the window is invisible — if refreshInterval is enabled — by using the refreshWhenHidden option, and when the browser is offline using the refreshWhenOffline option.

Revalidate on reconnect

This revalidation occurs when networks recover, e.g., in a scenario when the user is back online. It can be configured using the revalidateOnReconnect option.

In version 1, SWR provides a helper, the useSWRImmutable hook, to make state immutable. This means the state does not change when we revalidate.

The useSWRImmutable hook is similar to the useSWR hook and we can make state immutable using both as seen below:

import useSWR from 'swr'
import useSWRImmutable from 'swr/immutable'

useSWRImmutable(key, fetcher, options) // makes resourse immutable 

useSWR(key, fetcher, {
  revalidateIfStale: false,
  revalidateOnFocus: false,
  revalidateOnReconnect: false
})

// equivalent to
useSWRImmutable(key, fetcher) // makes resourse immutable 

In the code above, the two hooks make the data immutable once it is cached. useSWRImmutable does it automatically for us.

5. Middleware

The SWR middleware API provides a great way to abstract and reuse logic.

The middleware is a closure that receives the SWR hook as an argument and wraps around the useSWR hook, thereby enabling us to execute logic before and after invoking the useSWR hooks.

Consider the code below:

function SWRMiddleware (useSWRNext) {
  return (key, fetcher, config) => {
    // beform operations before invoking useSWR()

    // Handle the next middleware, or invokes `useSWR` hook if this is the last middleware.
    const swr = useSWRNext(key, fetcher, config)

    // befrom operations after invoking useSWR()
    return swr
  }
}

The SWR middleware is very useful and powerful, and a lot of ideas can be implemented using this feature. An example from the documentation is a request logger.

The logger middleware is a function decorator that decorates the fetcher function with the ability to log requests — the SWR key in this case:

function logger(useSWRNext) {
  return (key, fetcher, config) => {
    // Add logger to the original fetcher.
    const extendedFetcher = (...args) => {
      console.log('SWR Request:', key)
      return fetcher(...args)
    }

    // Execute the hook with the new fetcher.
    return useSWRNext(key, extendedFetcher, config)
  }
}

Consequently, when we use this middleware, the SWR key is logged to the console:

...
// ... used from inside your component
useSWR(key, fetcher, { use: [logger] })
SWR Request: /api/user1
SWR Request: /api/user2
...

To use this feature, we pass an array of middlewares to either SWRConfig or useSWR:

<SWRConfig value={{ use: [middlewareA, middleWareB, middlewareC] }}>

// or...

useSWR(key, fetcher, { use: [middlewareA, middleWareB, middlewareC] })

When dealing with multiple middlewares, each middleware is nested within the other and the last middleware receives the SWR hook.

6. Fallback data

This new feature simply enables us to provide arbitrary prefetched data for all SWR with specific keys:

<SWRConfig value={{
  fallback: {
    '/user': { name: 'Eagles', ... },
    '/posts': ...,
    '/pages': ...,
    ...
  }
}}>
  <App/>
</SWRConfig>

These prefetched data can be displayed as placeholder content on your page while your application revalidates. This improves the user experience and it is very helpful in scenarios such as static site generation (SSG) and server-side rendering (SSR).

Also, for consistency’s sake and to avoid confusion, the old initialData has been renamed to fallbackData but it still provides a single fallback value for the specified SWR hook.

Conclusion

SWR is an awesome, well-thought-out, lightweight, and fast data fetching library. It is transport- and protocol-agnostic — it supports both REST and GraphQL, it supports suspense, and it is TypeScript-ready. The patterns implemented in SWR make developers’ lives easier.

And SWR version 1 is definitely a must-use for every developer. You can learn more about version 1 here. You can also read the changelog for more information.

Full visibility into production React apps

Debugging React applications can be difficult, especially when users experience issues that are hard to reproduce. If you’re interested in monitoring and tracking Redux state, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, try LogRocket.

LogRocket is like a DVR for web apps, recording literally everything that happens on your React app. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app's performance, reporting with metrics like client CPU load, client memory usage, and more.

The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.

Modernize how you debug your React apps — .

Lawrence Eagles Senior full-stack developer, writer, and instructor.

Leave a Reply