Greg Rickaby

Full-Stack Developer

13 min read 路 #code

SWR Examples

SWR is an awesome React Hook library for remote data fetching, maintained by the team at Vercel.

Introduction

SWR is an awesome React Hook library for remote data fetching, maintained by the team at Vercel. SWR stands for "stale-while-revalidating", which means, SWR will attempt to load cached data (stale) first, and then fetch new data (revalidate) in the background.

The examples listed on the SWR website and Github are great, but for brevity, some of the them omit small bits and don't actually "do anything"; while other examples feel overly complex and assume we're all "10x developers". I put together this repo to teach myself how to use SWR, and I hope that these simplistic examples will help others. 馃嵒

Before jumping in, take a minute to read the following:

Preface

First, all the examples in this repo run on Next.js, which is one of two recommended toolchains by the React core team. Next.js has built-in support for Fetch, so you don't have to install a fetch library as a dependency. Just know, if you do copy/paste these examples into something like Create React App, you'll need to install and import a fetch library first.

Second, all examples use JSON.stringify() to display the fetched data. I didn't want to overcomplicate things with opinionated markup about displaying data. Chances are, you just need to .map() over the fetched data like this:

return (
  <>
    {data.items.map((item, index) => (
      <div key={index}>{item.title}</div>
    ))}
  </>
)

And finally, SWR needs something to actually fetch data. The fetcher() function below, is a quick one-liner used for example purposes throughout this repo. I wouldn't use this on a complex project.

const fetcher = (url) => fetch(url).then((r) => r.json())

Engage...

The API

Here is the full useSWR() hook:

const {data, error, isValidating, mutate} = useSWR(key, fetcher, options)

Parameters

  • key: A unique string (or function / array / null) for the request. Usually the URL of an API. (advanced usage)
  • fetcher: (optional) Any Promise returning function or library to fetch your data. (details)
  • options: (optional) An object of options for this SWR hook. (view all options)

Return Values

  • data: The data from the fetcher.
  • error: An error thrown by fetcher.
  • isValidating: If there's a request or revalidation loading.
  • mutate(data?, shouldRevalidate?): A function to mutate the cached data.

Examples

Basic Example with fetch()

In the basic example, let's fetch a person from the SWAPI:

import useSWR from 'swr'

const fetcher = (url) => fetch(url).then((r) => r.json())

export default function Example() {
  const {data, error} = useSWR(`https://swapi.dev/api/people/1/`, fetcher)

  if (error) return <div>failed to load</div>
  if (!data) return <div>loading...</div>
  return <pre>{JSON.stringify(data, null, 2)}</pre>
}

and we get the following response:

{
  "name": "Luke Skywalker",
  "height": "172",
  "mass": "77",
  "hair_color": "blond",
  "skin_color": "fair",
  "eye_color": "blue",
  "birth_year": "19BBY",
  "gender": "male",
  "homeworld": "https://swapi.dev/api/planets/1/",
  "films": [
    "https://swapi.dev/api/films/1/",
    "https://swapi.dev/api/films/2/",
    "https://swapi.dev/api/films/3/",
    "https://swapi.dev/api/films/6/"
  ],
  "species": [],
  "vehicles": [
    "https://swapi.dev/api/vehicles/14/",
    "https://swapi.dev/api/vehicles/30/"
  ],
  "starships": [
    "https://swapi.dev/api/starships/12/",
    "https://swapi.dev/api/starships/22/"
  ],
  "created": "2014-12-09T13:50:51.644000Z",
  "edited": "2014-12-20T21:17:56.891000Z",
  "url": "https://swapi.dev/api/people/1/"
}

View on CodeSandbox


Axios

Know what's cool about SWR? You're not restricted to just using fetch() to grab data from REST APIs. You can define any asynchronous function or library as the fetcher()!

In this example, we'll use the tried and true data fetching library, Axios to fetch a person from the SWAPI:

import useSWR from 'swr'
import axios from 'axios'

const fetcher = (url) => axios.get(url)

export default function Example() {
  const {data, error} = useSWR(`https://swapi.dev/api/people/1/`, fetcher)

  if (error) return <div>failed to load</div>
  if (!data) return <div>loading...</div>
  return <pre>{JSON.stringify(data, null, 2)}</pre>
}

and we get the following response:

{
  "data": {
    "name": "Luke Skywalker",
    "height": "172",
    "mass": "77",
    "hair_color": "blond",
    "skin_color": "fair",
    "eye_color": "blue",
    "birth_year": "19BBY",
    "gender": "male",
    "homeworld": "https://swapi.dev/api/planets/1/",
    "films": [
      "https://swapi.dev/api/films/1/",
      "https://swapi.dev/api/films/2/",
      "https://swapi.dev/api/films/3/",
      "https://swapi.dev/api/films/6/"
    ],
    "species": [],
    "vehicles": [
      "https://swapi.dev/api/vehicles/14/",
      "https://swapi.dev/api/vehicles/30/"
    ],
    "starships": [
      "https://swapi.dev/api/starships/12/",
      "https://swapi.dev/api/starships/22/"
    ],
    "created": "2014-12-09T13:50:51.644000Z",
    "edited": "2014-12-20T21:17:56.891000Z",
    "url": "https://swapi.dev/api/people/1/"
  },
  "status": 200,
  "statusText": "",
  "headers": {
    "content-type": "application/json"
  },
  "config": {
    "transitional": {
      "silentJSONParsing": true,
      "forcedJSONParsing": true,
      "clarifyTimeoutError": false
    },
    "transformRequest": [null],
    "transformResponse": [null],
    "timeout": 0,
    "xsrfCookieName": "XSRF-TOKEN",
    "xsrfHeaderName": "X-XSRF-TOKEN",
    "maxContentLength": -1,
    "maxBodyLength": -1,
    "headers": {
      "Accept": "application/json, text/plain, */*"
    },
    "method": "get",
    "url": "https://swapi.dev/api/people/1/"
  },
  "request": {}
}

View on CodeSandbox


GraphQL

Since SWR is fetching library agnostic, let's use another third-party library to fetch data. In this example, we'll use the graph-request library, to query a GraphQL endpoint, and display the titles of some WordPress pages.

const fetcher = (query) => request(`https://nextjs.wpengine.com/graphql`, query)

export default function Example() {
  const {data, error} = useSWR(
    `
      {
        pages {
          nodes {
            title
          }
        }
      }
    `,
    fetcher
  )

  if (error) return <div>failed to load</div>
  if (!data) return <div>loading...</div>
  return <pre>{JSON.stringify(data, null, 2)}</pre>
}

and we get the following response from WordPress:

{
  "pages": {
    "nodes": [
      {
        "title": "Careers"
      },
      {
        "title": "404 Not Found"
      },
      {
        "title": "About"
      },
      {
        "title": "Contact"
      },
      {
        "title": "Post Archive"
      },
      {
        "title": "Home"
      }
    ]
  }
}

View on CodeSandbox


Dependent Fetching

SWR allows you to fetch data that depends on other data, as well as serial fetching when a piece of dynamic data is required for the next data fetch to happen.

In this example, we're going to query a WordPress blog post and query for the post tag:

import useSWR from 'swr'

const fetcher = (url) => fetch(url).then((r) => r.json())

export default function Example() {
  // First, fetch a blog post...
  const {data: post} = useSWR(
    `https://webdevstudios.com/wp-json/wp/v2/posts/22342`,
    fetcher
  )

  // Then, fetch a tag from the blog post.
  const {data: tag} = useSWR(
    () => `https://webdevstudios.com/wp-json/wp/v2/tags/${post.tags[1]}`,
    fetcher
  )

  if (!tag) return 'loading...'

  return <pre>{JSON.stringify(tag.name, null, 2)}</pre>
}

and we get the following tag from WordPress:

"partnership"

View on CodeSandbox


Conditional Fetching

You can use a ternary operator in the key parameter to conditionally fetch data.

In this example, I use both the useState() and useEffect() hooks, along with setTimeout() to delay loading the data.

import {useState, useEffect} from 'react'
import useSWR from 'swr'

const fetcher = (url) => fetch(url).then((r) => r.json())

export default function Example() {
  // Set "sleeping" to true.
  const [sleeping, setSleeping] = useState(true)

  // Do not fetch until sleeping is false.
  const {data, error} = useSWR(
    sleeping ? null : `https://swapi.dev/api/people/1/`,
    fetcher
  )

  // After 3 seconds, setSleeping to false.
  useEffect(() => {
    setTimeout(() => {
      setSleeping(false)
    }, 3000)
  }, [])

  if (error) return <div>failed to load</div>
  if (!data) return <div>loading in 3 seconds...</div>
  return <pre>{JSON.stringify(data, null, 2)}</pre>
}

and after 3 seconds, we get the following response:

{
  "name": "Luke Skywalker",
  "height": "172",
  "mass": "77",
  "hair_color": "blond",
  "skin_color": "fair",
  "eye_color": "blue",
  "birth_year": "19BBY",
  "gender": "male",
  "homeworld": "https://swapi.dev/api/planets/1/",
  "films": [
    "https://swapi.dev/api/films/1/",
    "https://swapi.dev/api/films/2/",
    "https://swapi.dev/api/films/3/",
    "https://swapi.dev/api/films/6/"
  ],
  "species": [],
  "vehicles": [
    "https://swapi.dev/api/vehicles/14/",
    "https://swapi.dev/api/vehicles/30/"
  ],
  "starships": [
    "https://swapi.dev/api/starships/12/",
    "https://swapi.dev/api/starships/22/"
  ],
  "created": "2014-12-09T13:50:51.644000Z",
  "edited": "2014-12-20T21:17:56.891000Z",
  "url": "https://swapi.dev/api/people/1/"
}

View on CodeSandbox


React Suspense (Experimental)

When using the react@alpha and react-dom@alpha packages, you can load a <Suspense> component that waits for and displays a loading state (like a spinner) until all the data has loaded in the background. Learn more about React 18.

By passing { suspense: true } into SWR's options, you can leverage React Suspense for data fetching. In this example, let's fetch another person from the SWAPI, and display loading... why we wait:

import { Suspense } from "react";
import useSWR from "swr";

const fetcher = (url) => fetch(url).then((r) => r.json());

function Profile() {
  const { data } = useSWR(`https://swapi.dev/api/people/1/`, fetcher, {
    suspense: true,
  });
  return <pre>{JSON.stringify(data, null, 2)}</pre>;
};

export default function Example() {
  <Suspense fallback={<div>loading...</div>}>
    <Profile />
  </Suspense>
);

View on CodeSandbox


To learn more about SWR, visit the official website and check out Github to see all the examples.

Greg is the Director of Engineering at WebDevStudios. He also moonlights at Dummies writing and editing books. Follow him on Twitter for lots of pictures of pepperoni pizza and tidbits about Next.js.