SWR, an automatically refreshing datasource for React and Next.js

by
Tags: , ,
Category:

SWR, a React-friendly API used both stand-alone and by Vercel's Next.js platform, is a caching data manager. It can fetch anything, owing to the API's fetcher delegating network querying API. It provides intelligent data caching, pagination, support for the React Suspense component, and a number of other features.

In this article I'll show you a simple interaction with a dynamically changing NodeJS RESTful data resource via Next.js, using SWR to fetch, cache and refresh the data.

Motivations

Data from server resources frequently change after being fetched by a client. Many times your application needs to keep its data up to date. For example, changing weather forecasts, financial data feeds, live dashboards, and many other services rely on auto refreshed data.

Rather than roll your own feature using your own timers and cache busting tools, you can rely on SWR to manage these feeds for you.

SWR for Dynamic Fetching

With SWR (which stands for "stale while revalidate"), a data fetching API (such as Fetch, Axios, or even GraphQL) is wrapped with a "Fetcher" facade. This facade is called upon by the SWR hook (useSWR) to bind data functionally to your React components, which is then kept up to date based on settings in the SWR configuration API.

Let's start by defining a network feed to monitor, using the useSWR hook. My arbitrary example, available at GitHub, uses a NodeJS resource server to serve up quotes to a React app:

export default function SwrDemoComponent() {
  const { data, error } = useSWR('/api/cms/demo');
  return {
    { !error && data && <p>Data! {data}</p> }
  };
};

As with other hooks, the useSWR hook returns multiple properties to work with:

  • data – the data fetched by the hook
  • error – any error that results from fetching the data
  • isValidating – a boolean to show that the fetcher is active
  • mutate – a method to call to invalidate any cache and request a data refresh from SWR-fetched data

Building a data fetcher

Here is our fetcher API, used when configuring SWR. It fetches the resource using the browser's Fetch API, which in Next.js is backed up by a server-side equivalent:

export async function fetcher (resource: string) {
  let result;
  try {
    result = await fetch(resource);
  } catch (e) {
    console.log('***** Problem with fetch that results in an exception');
    console.error(e);
    throw new Error('Invalid Response');
  }
  if (result.ok) {
    try {
      return await result.json();
    } catch (e) {
      console.log('***** Problem with JSON payload', e);
      throw 'Result OK but JSON borked';
    }
  } else {
    console.log('****** Result ! OK', result.status, result.statusText);
    throw result.statusText;
  }
}

In the fetcher above, we handle several error conditions. For status errors, we unpack and throw an error containing the statusText property of the response. For invalid data responses, we throw an error with the message that the JSON is invalid. Finally, if the network stack fails (i.e. the server is offline) we also handle that error as well as an "Invalid Response".

Installing the fetcher in SWR

Fetchers can be used directly from the useSWR hook:

const { data, error } = useSWR('/api/foobar', fetcher);

Rather than attaching the fetcher to every hook, we can use a component to wrap all useSWR usages and attach the middleware there. This example uses the Next.js page template file, _app.tsx, to install the fetcher for the entire application, and also sets some global settings:

import '../styles/globals.css'
import type { AppProps } from 'next/app'
import { fetcher as myFetcher } from '../shared/fetcher';
import { SWRConfig } from 'swr';

function MyApp({ Component, pageProps }: AppProps) {
  return (
    <SWRConfig value={{
      refreshInterval: 10000,
      fetcher: myFetcher
    }}>
      <Component {...pageProps} />
    </SWRConfig>
  );
}

export default MyApp

SWR options

Yes, the example above sets a 10 second auto-refresh interval. By default, SWR will reload data when:

  • The user switches tabs in a browser and then switches back to the application containing the SWR hook
  • the user goes offline and then online again

Adding the refresh interval makes it possible for data to be displayed once cached, and to be replaced once a new payload appears on the network, within the interval.

Other SWR options include:

  • suspense – enable React Suspense support
  • revalidateIfStale – once a component mounts, by default SWR refreshes the content. You can disable by passing false to this property
  • revalidateOnFocus – set to false to disable the focus-based refresh
  • refreshWhenHidden and refreshWhenOffline for additional options for keeping the data fresh (these are disabled by default)

And many more. Interestingly, SWR will automatically retry when a network error occurs (5 seconds by default – errorRetryInterval and you can set the maximum number of retries (errorRetryCount).

See the options page for more details.

Wrap-up

Check out SWR on Vercel's site for more details, including special usages on Next.js, some really powerful paging features, injecting middleware, and more. I've prepared a simple demo app to play around with SWR at github.com/krimple/swr-demo.