React Hooks library for remote data fetching

Last update: Aug 2, 2022

SWR


Introduction

swr.vercel.app

SWR is a React Hooks library for remote data fetching.

The name “SWR” is derived from stale-while-revalidate, a cache invalidation strategy popularized by HTTP RFC 5861. SWR first returns the data from cache (stale), then sends the fetch request (revalidate), and finally comes with the up-to-date data again.

It features:

  • Transport and protocol agnostic data fetching
  • Fast page navigation
  • Revalidation on focus
  • Interval polling
  • Request deduplication
  • Local mutation
  • Pagination
  • TypeScript ready
  • SSR support
  • Suspense mode
  • React Native support
  • Minimal API

...and a lot more.

With SWR, components will get a stream of data updates constantly and automatically. Thus, the UI will be always fast and reactive.


Quick Start

import useSWR from 'swr'

function Profile() {
  const { data, error } = useSWR('/api/user', fetcher)

  if (error) return <div>failed to load</div>
  if (!data) return <div>loading...</div>
  return <div>hello {data.name}!</div>
}

In this example, the React Hook useSWR accepts a key and a fetcher function. The key is a unique identifier of the request, normally the URL of the API. And the fetcher accepts key as its parameter and returns the data asynchronously.

useSWR also returns 2 values: data and error. When the request (fetcher) is not yet finished, data will be undefined. And when we get a response, it sets data and error based on the result of fetcher and rerenders the component.

Note that fetcher can be any asynchronous function, so you can use your favourite data-fetching library to handle that part.

Check out swr.vercel.app for more demos of SWR, and Examples for the best practices.


Usage

Inside your React project directory, run the following:

yarn add swr

Or with npm:

npm install swr

API

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

Parameters

  • key: a unique key string for the request (or a function / array / null) (advanced usage)
  • fetcher: (optional) a Promise returning function to fetch your data (details)
  • options: (optional) an object of options for this SWR hook

Return Values

  • data: data for the given key resolved by fetcher (or undefined if not loaded)
  • error: error thrown by fetcher (or undefined)
  • isValidating: if there's a request or revalidation loading
  • mutate(data?, shouldRevalidate?): function to mutate the cached data

Options

  • suspense = false: enable React Suspense mode (details)
  • fetcher = window.fetch: the default fetcher function
  • initialData: initial data to be returned (note: This is per-hook)
  • revalidateOnMount: enable or disable automatic revalidation when component is mounted (by default revalidation occurs on mount when initialData is not set, use this flag to force behavior)
  • revalidateOnFocus = true: auto revalidate when window gets focused
  • revalidateOnReconnect = true: automatically revalidate when the browser regains a network connection (via navigator.onLine)
  • refreshInterval = 0: polling interval (disabled by default)
  • refreshWhenHidden = false: polling when the window is invisible (if refreshInterval is enabled)
  • refreshWhenOffline = false: polling when the browser is offline (determined by navigator.onLine)
  • shouldRetryOnError = true: retry when fetcher has an error (details)
  • dedupingInterval = 2000: dedupe requests with the same key in this time span
  • focusThrottleInterval = 5000: only revalidate once during a time span
  • loadingTimeout = 3000: timeout to trigger the onLoadingSlow event
  • errorRetryInterval = 5000: error retry interval (details)
  • errorRetryCount: max error retry count (details)
  • onLoadingSlow(key, config): callback function when a request takes too long to load (see loadingTimeout)
  • onSuccess(data, key, config): callback function when a request finishes successfully
  • onError(err, key, config): callback function when a request returns an error
  • onErrorRetry(err, key, config, revalidate, revalidateOps): handler for error retry
  • compare(a, b): comparison function used to detect when returned data has changed, to avoid spurious rerenders. By default, dequal/lite is used.
  • isPaused(): function to detect whether pause revalidations, will ignore fetched data and errors when it returns true. Returns false by default.

When under a slow network (2G, <= 70Kbps), errorRetryInterval will be 10s, and loadingTimeout will be 5s by default.

You can also use a global configuration to provide default options.


Examples

Global Configuration

The context SWRConfig can provide global configurations (options) for all SWR hooks.

In this example, all SWRs will use the same fetcher provided to load JSON data, and refresh every 3 seconds by default:

import useSWR, { SWRConfig } from 'swr'

function Dashboard() {
  const { data: events } = useSWR('/api/events')
  const { data: projects } = useSWR('/api/projects')
  const { data: user } = useSWR('/api/user', { refreshInterval: 0 }) // don't refresh
  // ...
}

function App() {
  return (
    <SWRConfig
      value={{
        refreshInterval: 3000,
        fetcher: (...args) => fetch(...args).then(res => res.json())
      }}
    >
      <Dashboard />
    </SWRConfig>
  )
}

Data Fetching

fetcher is a function that accepts the key of SWR, and returns a value or a Promise. You can use any library to handle data fetching, for example:

import fetch from 'unfetch'

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

function App() {
  const { data } = useSWR('/api/data', fetcher)
  // ...
}

Or using GraphQL:

import { request } from 'graphql-request'

const fetcher = query => request('/api/graphql', query)

function App() {
  const { data, error } = useSWR(
    `{
      Movie(title: "Inception") {
        releaseDate
        actors {
          name
        }
      }
    }`,
    fetcher
  )
  // ...
}

If you want to pass variables to a GraphQL query, check out Multiple Arguments.

Note that fetcher can be omitted from the parameters if it's provided globally.

Conditional Fetching

Use null or pass a function as the key to useSWR to conditionally fetch data. If the functions throws an error or returns a falsy value, SWR will cancel the request.

// conditionally fetch
const { data } = useSWR(shouldFetch ? '/api/data' : null, fetcher)

// ...or return a falsy value
const { data } = useSWR(() => shouldFetch ? '/api/data' : null, fetcher)

// ... or throw an error when user.id is not defined
const { data } = useSWR(() => '/api/data?uid=' + user.id, fetcher)

Dependent Fetching

SWR also allows you to fetch data that depends on other data. It ensures the maximum possible parallelism (avoiding waterfalls), as well as serial fetching when a piece of dynamic data is required for the next data fetch to happen.

function MyProjects() {
  const { data: user } = useSWR('/api/user')
  const { data: projects } = useSWR(() => '/api/projects?uid=' + user.id)
  // When passing a function, SWR will use the return
  // value as `key`. If the function throws or returns
  // falsy, SWR will know that some dependencies are not
  // ready. In this case `user.id` throws when `user`
  // isn't loaded.

  if (!projects) return 'loading...'
  return 'You have ' + projects.length + ' projects'
}

Multiple Arguments

In some scenarios, it's useful to pass multiple arguments (can be any value or object) to the fetcher function. For example:

useSWR('/api/user', url => fetchWithToken(url, token))

This is incorrect. Because the identifier (also the index of the cache) of the data is '/api/user', so even if token changes, SWR will still have the same key and return the wrong data.

Instead, you can use an array as the key parameter, which contains multiple arguments of fetcher:

const { data: user } = useSWR(['/api/user', token], fetchWithToken)

// ...and pass it as an argument to another query
const { data: orders } = useSWR(user ? ['/api/orders', user] : null, fetchWithUser)

The key of the request is now the combination of both values. SWR shallowly compares the arguments on every render and triggers revalidation if any of them has changed. Keep in mind that you should not recreate objects when rendering, as they will be treated as different objects on every render:

// Don’t do this! Deps will be changed on every render.
useSWR(['/api/user', { id }], query)

// Instead, you should only pass “stable” values.
useSWR(['/api/user', id], (url, id) => query(url, { id }))

Dan Abramov explains dependencies very well in this blog post.

Manually Revalidate

You can broadcast a revalidation message globally to all SWRs with the same key by calling mutate(key).

This example shows how to automatically refetch the login info (e.g.: inside <Profile/>) when the user clicks the “Logout” button.

import useSWR, { mutate } from 'swr'

function App() {
  return (
    <div>
      <Profile />
      <button onClick={() => {
        // set the cookie as expired
        document.cookie = 'token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;'

        // tell all SWRs with this key to revalidate
        mutate('/api/user')
      }}>
        Logout
      </button>
    </div>
  )
}

Mutation and Post Request

In many cases, applying local mutations to data is a good way to make changes feel faster — no need to wait for the remote source of data.

With mutate, you can update your local data programmatically, while revalidating and finally replace it with the latest data.

import useSWR, { mutate } from 'swr'

function Profile() {
  const { data } = useSWR('/api/user', fetcher)

  return (
    <div>
      <h1>My name is {data.name}.</h1>
      <button onClick={async () => {
        const newName = data.name.toUpperCase()
        
        // update the local data immediately, but disable the revalidation
        mutate('/api/user', { ...data, name: newName }, false)
        
        // send a request to the API to update the source
        await requestUpdateUsername(newName)
        
        // trigger a revalidation (refetch) to make sure our local data is correct
        mutate('/api/user')
      }}>Uppercase my name!</button>
    </div>
  )
}

Clicking the button in the example above will send a POST request to modify the remote data, locally update the client data and try to fetch the latest one (revalidate).

But many POST APIs will just return the updated data directly, so we don’t need to revalidate again. Here’s an example showing the “local mutate - request - update” usage:

mutate('/api/user', newUser, false)      // use `false` to mutate without revalidation
mutate('/api/user', updateUser(newUser)) // `updateUser` is a Promise of the request,
                                         // which returns the updated document

Mutate Based on Current Data

In many cases, you are receiving a single value back from your API and want to update a list of them.

With mutate, you can pass an async function which will receive the current cached value, if any, and let you return an updated document.

mutate('/api/users', async users => {
  const user = await fetcher('/api/users/1')
  return [user, ...users.slice(1)]
})

Returned Data from Mutate

Most probably, you need some data to update the cache. The data is resolved or returned from the promise or async function you passed to mutate.

The function will return an updated document to let mutate update the corresponding cache value. It could throw an error somehow, every time when you call it.

try {
  const user = await mutate('/api/user', updateUser(newUser))
} catch (error) {
  // Handle an error while updating the user here
}

Bound mutate()

The SWR object returned by useSWR also contains a mutate() function that is pre-bound to the SWR's key.

It is functionally equivalent to the global mutate function but does not require the key parameter.

import useSWR from 'swr'

function Profile() {
  const { data, mutate } = useSWR('/api/user', fetcher)

  return (
    <div>
      <h1>My name is {data.name}.</h1>
      <button onClick={async () => {
        const newName = data.name.toUpperCase()
        // send a request to the API to update the data
        await requestUpdateUsername(newName)
        // update the local data immediately and revalidate (refetch)
        // NOTE: key is not required when using useSWR's mutate as it's pre-bound
        mutate({ ...data, name: newName })
      }}>Uppercase my name!</button>
    </div>
  )
}

SSR with Next.js

With the initialData option, you pass an initial value to the hook. It works perfectly with many SSR solutions such as getServerSideProps in Next.js:

export async function getServerSideProps() {
  const data = await fetcher('/api/data')
  return { props: { data } }
}

function App(props) {
  const initialData = props.data
  const { data } = useSWR('/api/data', fetcher, { initialData })

  return <div>{data}</div>
}

It is still a server-side rendered site, but it’s also fully powered by SWR in the client-side. Which means the data can be dynamic and update itself over time and user interactions.

Suspense Mode

You can enable the suspense option to use SWR with React Suspense:

import { Suspense } from 'react'
import useSWR from 'swr'

function Profile() {
  const { data } = useSWR('/api/user', fetcher, { suspense: true })
  return <div>hello, {data.name}</div>
}

function App() {
  return (
    <Suspense fallback={<div>loading...</div>}>
      <Profile/>
    </Suspense>
  )
}

In Suspense mode, data is always the fetch response (so you don't need to check if it's undefined). But if an error occurred, you need to use an error boundary to catch it.

Note that Suspense is not supported in SSR mode.

Error Retries

By default, SWR uses the exponential backoff algorithm to handle error retries. You can read more from the source code.

It's also possible to override the behavior:

useSWR(key, fetcher, {
  onErrorRetry: (error, key, option, revalidate, { retryCount }) => {
    if (retryCount >= 10) return
    if (error.status === 404) return

    // retry after 5 seconds
    setTimeout(() => revalidate({ retryCount: retryCount + 1 }), 5000)
  }
})

Prefetching Data

There’re many ways to prefetch the data for SWR. For top-level requests, rel="preload" is highly recommended:

<link rel="preload" href="/api/data" as="fetch" crossorigin="anonymous">

This will prefetch the data before the JavaScript starts downloading. And your incoming fetch requests will reuse the result (including SWR, of course).

Another choice is to prefetch the data conditionally. You can have a function to refetch and set the cache:

function prefetch() {
  mutate('/api/data', fetch('/api/data').then(res => res.json()))
  // the second parameter is a Promise
  // SWR will use the result when it resolves
}

And use it when you need to preload the resources (for example when hovering a link). Together with techniques like page prefetching in Next.js, you will be able to load both next page and data instantly.

Request Deduplication

SWR deduplicates requests by default. If you call the hook with the same key multiple times, only one request is made. Duplicated calls will receive a value from cache. Here, the 'api/user' key is used in two requests:

import useSWR from 'swr'

function UserProfileName() {
  const { data, error } = useSWR('/api/user', fetcher)

  if (error) return <div>failed to load</div>
  if (!data) return <div>loading...</div>
  return <p>Name: {data.name}!</p>
}

function UserProfileAvatar() {
  const { data, error } = useSWR('/api/user', fetcher)

  if (error) return <div>failed to load</div>
  if (!data) return <div>loading...</div>
  return <img src={data.avatarUrl} alt="Profile image" />
}

export default function App() {
  return (
    <div>
      <UserProfileName />
      <UserProfileAvatar />
    </div>
  )
}

By default, requests made within 2 seconds are deduped. This can be changed by setting the dedupingInterval option:

const { data, error } = useSWR('/api/user', fetcher, { dedupingInterval: 1000 })

This will deduplicate requests at an interval of 1 second.

Authors

Thanks to Ryan Chen for providing the awesome swr npm package name!


License

The MIT License.

GitHub

https://github.com/zeit/swr
Comments
  • 1. New infinite loading API

    This PR adds a new useSWRInfinite API for infinite loading, which is far more stable and easier to understand and use:

    const { data, error, mutate, size, setSize } = useSWRInfinite(getKey, fetcher?, options?)
    

    The new API signature is very similar to useSWR. One difference is that the first parameter getKey is a function that returns the key:

    (currentPageIndex, previousPageData) => keyOfThisPage
    

    API

    The returned object includes size and setSize, they are returned by an internal state inside the hook.

    • data: an array of paginated fetch response values.
    • mutate: same as useSWR's bound mutate function[1]
    • options: all current SWR options are supported, with 3 new options:
      • initialSize: number of pages should be loaded initially, default to 1
      • revalidateAll: always try to revalidate all pages[2], default to false
      • persistSize: don't reset the page size to 1 (or initialSize if set) when the first page's key changes #490

    [1]: note that the global mutate won't work with this new API [2]: useSWRInfinite supports all the revalidation options of SWR too (refreshInterval, revalidateOnFocus, revalidateOnReconnect...). However by default it will only revalidate the first page. You can change the behavior to revalidate all pages by setting revalidateAll to true.

    Why Renaming

    There're a lot of confusion about useSWRPages that people are using it for normal pagination use cases. However that's not necessary and you should implement it with page components or dynamic SWR keys.

    useSWRInfinite should only be used when you need dynamic number of fetch requests (a list that you can load more and append data to), and the key of each request depends on the previous data.

    Example 1: Paginated API

    GET /users?page=0&limit=10
    [
      { name: 'Alice' },
      { name: 'Bob' },
      { name: 'Cathy' },
      ...
    ]
    

    With useSWRInfinite:

    const { data } = useSWRInfinite(
      (index, previousPageData) => {
        // reached the end
        if (previousPageData && previousPageData.length === 0) return null
    
        // API key
        return `/users?page=${index}&limit=10`
      },
      fetcher, // same as useSWR
      opts     // same as useSWR
    )
    
    // render
    if (!data) return 'loading'
    return <>
      {data.map((users, index) => {
        // render each page
        return users.map(user => <div>{user.name}</div>)
      })}
    </>
    

    Example 2: Cursor Based Paginated API

    GET /users?cursor=123&limit=10
    {
      data: [
        { name: 'Alice' },
        { name: 'Bob' },
        { name: 'Cathy' },
        ...
      ],
      nextCursor: 456
    }
    

    With useSWRInfinite:

    const { data } = useSWRInfinite(
      (index, previousPageData) => {
        // reached the end
        if (previousPageData && previousPageData.data.length === 0) return null
    
        // first page, previousPageData is null
        if (index === 0) return `/users?limit=10`
    
        return `/users?cursor=${previousPageData.nextCursor}&limit=10`
      },
      fetcher, // same as useSWR
      opts.    // same as useSWR
    )
    

    Example 3: Advanced Features

    Here's an example showing how you can use this API to implement:

    • loadMore
    • isLoadingMore
    • isLoadingInitialData
    • isEmpty
    • isReachingEnd
    • change data source
    • refresh the entire list

    https://codesandbox.io/s/swr-infinite-z6r0r?file=/src/App.js

    Checklist

    • [x] cache page count
    • [x] scroll position restoration
    • [x] #391
    • [x] make setSize return promise
    • [x] update documentation (https://github.com/vercel/swr-site/pull/24)
    • [x] create examples (https://github.com/vercel/swr-site/pull/24)
    • [x] deprecate useSWRPages

    Latest release of this PR: [email protected].


    Related to #21, #70, #126, #133, #178, #189, #202, #205, #243, #246, #269, #289, #309, #311, #343, #347, #384, #396, #399, #434 (almost all our existing issues are about the undocumented pagination API 🙊).

    Reviewed by shuding at 2020-06-08 11:49
  • 2. Data is not updated with `initialData`

    I'm on a Next.js app and here is my (simplified) code:

    const IndexPage = ({ data: initialData }) => {
      const [filters, setFilters] = useState(defaultFilters)
    
      const onChange = () => {
        ...
        setFilters(newFilters)
      }
    
      const query = getQuery(filters)
      const { data } = useSWR(`/api/resorts?${query}`, fetcher, { initialData })
    
      return (...)
    }
    
    Index.getInitialProps = async ctx => {
      const query = getQuery(defaultFilters)
      const data: Resort[] = await fetcher(`${getHost(ctx)}/api/resorts?${query}`)
      return { data }
    }
    

    I have an initial set of filter (defaultFilters) on which I query and pass to useSWR with initialData on first render. When the user changes one filter, the key should change as it's a new query, but useSWR still returns the old data, from the first call.

    When I remove initialData from useSWR it works but that's not what I want, I want to have SSR.

    Am I doing something wrong?

    Reviewed by Kerumen at 2020-02-28 00:43
  • 3. Expose Cache

    This attempts to partially resolve #212 and resolves #161 and resolves #237 and resolves #158

    Implementation Notes

    I updated this to only expose the global cache, I kept the cache interface, adding a way to tell the set, delete and clear methods if you want to notify components reading from the cache, this way mutate can call cache.set and cache.set can also call mutate as a notification method without running on a infinite loop (mutate calls cache.set with this flag as false).

    I did this to come back to custom cache once I found a way to let mutate know which cache to call without manually passing it.

    ~I created a CacheInterface and make the global config create a default instance of the cache, also exported both the interface and the Cache class.~

    ~I also updated useSWR to read from the cache instance received through the context object.~

    ~I was not able, yet, to find a way to make mutate and trigger read from the correct cache instance, because they are global my only idea so far was to pass the cache instance as a third argument (probably optional), but that could make it hard to test components using a custom cache for tests and a default cache for the mutate call.~

    Missing things

    ~- useSWRPages is still using global cache (I haven't tried yet to use it here but I believe is the same as mutate and trigger)~ ~- mutate is still using global cache~ ~- trigger is still using global cache~

    Any feedback is welcomed!

    Reviewed by sergiodxa at 2020-01-14 23:13
  • 4. Possible to support object keys?

    Would it be possible to support objects as keys rather than forcing everything to be a string?

    In my case I'm looking to do something like

    const { data } = useSwr(userDetail.prepare({ id: 5 }));
    

    which returns the same object given the same inputs. The object itself has a method on it that is called by the fetcher:

    function fetcher(preparedAction) {
        return preparedAction.call();
    }
    

    As it is this doesn't work because the object returned is forced to a string here: https://github.com/zeit/swr/blob/master/src/use-swr.ts#L66

    Given the cache is a Map using an object as a key works, eg. changing that line to the following appears to do what I want:

    else if (!key || typeof key !== 'object') {
            // convert null to ''
            key = String(key || '');
    }
    

    Wrapping it in an array does also work:

    const { data } = useSwr([userDetail.prepare({ id: 5 })]);
    

    but the ergonomics aren't as good (especially given if you forget to wrap it in an array it just doesn't work - no errors).

    Unfortunately other things assume it's a string as well - eg. CONCURRENT_PROMISES etc are just plain objects rather than a Map.

    Is this something that you would be willing to extend the API to accomodate?

    My alternative to make this work within the current API constraints would be to have prepare return a string that fetcher can look up into my own global cache... but I was thinking it would be nice to avoid this if swr already did that internally.

    Reviewed by davecoates at 2019-12-02 00:43
  • 5. Keep previous result while revalidating

    Is there an easy way, or if one could be added, to have useSWR keep the previous result while revalidating?

    My case is a search field, and after I've gotten my first data, I'd really like the returned data to stick until a new request has completed (or failed). Currently the data goes back to undefined whenever the key changes.

    Reviewed by Svish at 2019-12-10 09:09
  • 6. Is there a way to reset pages of `useSWRPages`?

    Hi

    I'm using swr for pagination in my local next.js project. My implementation is based on this example. The difference is that my component with useSWRPages is using dynamic route, which loads user's feed, and the route looks like this: /user/[username].

    The problem appears when I switch user feed with loaded pages. In more details:

    1. I, as a user, open user-A's feed, then load a number of pages (let's say 4).
    2. I change feed to user-B's.
    3. User-B's feed loads 4 pages, instead of 1.

    I'd tried to pass username in to useSWRPages's deps, and use it in key, but it didn't helped me. Is there any workarounds? Maybe I should pass something else in to the deps?

    There's is an issue that seems like similar to mine: #126.

    Reviewed by tem-tem at 2019-12-09 09:32
  • 7. Have a way to clear cache

    I'm writing tests for a component using useSWR, also with { suspense: true }, and if I render a component reading the same key (e.g. the same component with different keys) I'm getting the old cache in my second test.

    Not sure if this will be addressed with #158, but having a way to tell SWR to clear the cache of a certain key would be awesome for this use case.

    import { clearCache } from "swr";
    
    clearCache(); // clear all cache
    clearCache("/api/data"); // clear only a certain cache key
    

    ☝️ something like that.

    What I have tried:

    • mutate(key, null, false)
    • mutate(key, {}, false)

    Both doesn't work right since useSWR will still read that null or {} from the cache instead of triggering an immediate fetch and suspending the component.

    I will try to create a CodeSandbox to replicate this soon.

    Reviewed by sergiodxa at 2019-11-28 17:07
  • 8. Add subscription support to useSWR

    This PR adds subscription support as proposed in https://github.com/vercel/swr/discussions/437#discussioncomment-25793

    The way it works uses the same API

    const { data } = useSWR("/api/messages", fetcher, {
      subscribe(key, mutate) {
        return subscribeToSomething(key, mutate);
      },
    });
    

    API

    Subscribe is a function receiving key and mutate.

    • key the unserialized key, what useSWR receives, it could be a string, array or function.
    • mutate is the same useSWR returns, already bound to the key

    The subscribe function must return an unsubscribe function, this unsubscribe function will be called in the case:

    1. The component using this useSWR unmount
    2. The subscribe fn is created again (it must be wrapped in useCallback to avoid this)
    3. The key changed

    A global subcribe function could be also used by setting it in SWRConfig.

    Examples

    WebSockets

    The most basic example is to use it go subscribe via WebSockets and get new data, e.g. in a chat application.

    const { data } = useSWR("/api/chat-messages", fetcher, {
      subscribe(key, mutate) {
        // start connection to the WebSocket API
        const ws = new WebSocket("wss://my.app/api/chat-messages");
        ws.onmessage = (event) => {
          mutate((messages) => [...messages, JSON.parse(event.data)], false);
        };
        return () => ws.close();
      },
    });
    

    Geolocation API

    We could use it to get new data from any type of stream, in this case Geolocation changes

    function fetcher() {
      return new Promise((resolve, reject) =>
        navigator.geolocation.getCurrentPosition(resolve, reject)
      );
    }
    
    const { data } = useSWR("geolocation", fetcher, {
      subscribe(_, mutate) {
        const id = navigator.geolocation.watchPosition((data) =>
          mutate(data, false)
        );
        return () => navigator.geolocation.clearWatch(id);
      },
    });
    

    Firebase (and other real-time databases)

    We could use it in combination with real-time databases like Firebase to get the current value in the fetcher and subscribe to updates for that same value.

    async function fetcher() {
      const ref = db.collection("cities").doc("SF");
      const doc = await ref.get();
      return doc.data();
    }
    
    function subscribe(_, mutate) {
      const ref = db.collection("cities").doc("SF");
      ref.on("value", (snapshot) => {
        mutate(snapshot.val(), false);
      });
      return ref.off;
    }
    
    const { data } = useSWR("db/cities/SF", fetcher, { subscribe });
    

    GraphQL

    We could use it with GraphQL to start a subscription in the same useSWR call.

    const { data } = useSWR(
      [gql`SOME GRAPHQL QUERY`, variables],
      (query, variables) => client.fetch(gql, { variables }),
      {
        subscribe([_, variables], mutate) {
          return client.subscribe(gql`SOME GRAPHQL SUBSCRIPTION QUERY`, {
            variables,
          });
        },
      }
    );
    

    LocalStorage

    It's also possible to use it to listen to global events like the storage one and get updates when another tab change the value in storage.

    const { data } = useSWR("storage-key", key => localStorage.get(key), {
      subscribe(key, mutate) {
        const listener = event => {
          if (event.key !== key) return;
          mutate(localStorage.get(key), false)
        });
        window.addEventListener("storage", listener);
        return () => window.removeEventListener("storage", listener);
      }
    });
    

    Checklist

    • [x] Add documentation
    • [x] Add examples to the folder
    Reviewed by sergiodxa at 2020-06-13 08:16
  • 9. Better support for react-native

    Hi guys, it could be great to use AppState (https://reactnative.dev/docs/appstate) for revalidateOnFocus and NetInfo (https://github.com/react-native-community/react-native-netinfo) for revalidateOnReconnect, errorRetryInterval, loadingTimeout when running in react-native context.

    Any advice with that? im handling revalidations manually for this cases.

    Thanks !!!

    Reviewed by outaTiME at 2020-05-31 22:41
  • 10. Support multiple caching mechanisms

    Frontend based:

    • Memory (Map, the current cache mechanism)
    • Session Storage
    • Local Storage
    • WebSQL
    • IndexedDb

    Backend Based:

    • sqlite
    • LRUCache
    • Redis
    • Anything...?

    Suggestion

    My suggestion would be to allow providing a class that implements a new interface that supports a similar API to Map (get, set, delete?), defaulting to a MemoryCache that just uses Map

    Side Notes

    Frontend based caches could help add support for persistence. Backend based caches could help with adding support for SSR.

    This could also add the possibility for supporting TTLs if that's desired, potentially solving #4 Can also solve #7 by allowing users the track their own namespaces.

    As suggested by @netspencer, a layered cache architecture could be awesome too

    After reading #4 it looks like you might already be planning this, which is awesome!

    Reviewed by aequasi at 2019-10-29 05:32
  • 11. useSWR to fetch only one time

    How can I use swr to fetch data only once? I don't need to refresh the data and revalidate. I prefer to use swr library for this and not just sending simple request and showing data, for couple of reasons:

    1. swr manages cache
    2. swr prevent a situation of update after unmount (no need to cancel request) and more great things swr has to offer..

    So how can I use swr for this? What I need to put in the options param?

    Is it a good idea to use it for this purpose? It feels like I'm using a sledgehammer to crack a nut.

    Thanks!

    Reviewed by oran1248 at 2020-06-10 18:10
  • 12. Silent error in getKey within useSWRInfinite

    Bug report

    Description / Observed Behavior

    useSWRInfinite hides errors within getKey (at least on the first page load). The error in getKey seems to cause the fetcher not to run. Because the error is hidden, it's difficult to debug.

    Expected Behavior

    I expect it to raise or display the error

    Repro Steps / Code Example

    import useSWRInfinite from "swr/infinite";
    
    useSWRInfinite(                    
      () => {                          
        throw new Error();             
      },                               
      () => fetch("http://example.com")
    );                                 
    

    Additional Context

    [email protected]

    Reviewed by onlyanegg at 2022-08-03 20:56
  • 13. rollbackOnError as function

    Discussed in https://github.com/vercel/swr/discussions/2092

    Originally posted by RenaudAubert July 29, 2022 Hi 👋 First of all a big thanks for this amazing library.

    I've noticed that 2 of the mutate options (populateCache, optimisticData) can accept functions and I was wondering if it'd make sense to also have this possibility for rollbackOnError?

    Use Case Let's say I have a mobile app that can be used in areas with a bad network coverage. When user performs an action thanks to optimisticData the cache and view are updated directly and if an error occurs with rollbackOnError data is rolled-back. But errors can occur for many reasons, sometimes the API returns a valid error sometimes the error come from the network for instance TimeoutError.

    I thought maybe something like this

    const newData = createNewData(...);
    mutate(key, updateFn(newData), {
        revalidate: true,
        populateCache: true,
        optimisticData: newData,
        rollbackOnError(error, currentData) {
            if (error.name === "TimeOutError") {
                return true;
            }
    
            return false;
        },
    });
    

    I'm not sure such an implementation could work, please feel free to let me know or suggest alternatives. Maybe I'm going at it completely wrong. What I'm currently implementing is something like the following

    const newData = createNewData(...);
    try {
        const mutatedData = await mutate(key, data, {
            populateCache: true,
            optimisticData: newData,
            rollbackOnError: false,
        });
    } catch (error) {
        // Error might comme from unique constraint in database, unsufficient permissions, etc...
        if (error.name !== "TimeoutError") {
            mutate(key, newData, {
                revalidate: false,
                populateCache: true,
                rollbackOnError: false,
            });
        }
    }
    
    Reviewed by huozhi at 2022-07-29 09:31
  • 14. reValidateOnMount doesn't work when component is unmounted while the data is being fetched

    Bug report

    Description / Observed Behavior

    Unmounting the component while the data is being fetched i.e; isValidating is true, doesn't trigger refetch on the next mount, even if reValidateOnMount is set to true.

    Expected Behavior

    Refetch should happen on each mount if reValidateOnMount is set to true.

    Additional Context

    • Version: 0.5.6
    • Framework: React Native
    Reviewed by sibasishm at 2022-07-21 12:37
  • 15. Make it possible to infer the fetcher's key

    Hi!

    I am using passing objects feature (https://swr.vercel.app/docs/arguments#passing-objects) like this.

    image

    Unfortunately, This obviously leads to incorrect execution. But you won't know until execute project.

    because this fetcher infers the type as a bare fetcher, it shows no error information.

    but, enable key inference with this update,

    image

    can be corrected correctly

    image

    I looked at swr2 (also 1.3), If only a fetcher and a key exist, there is no need to infer that it is a bare fetcher. so I think it would be better to inform the user that this is an incorrect usage.

    Please consider this change. thank you for read!

    Reviewed by Yuddomack at 2022-07-10 17:00
  • 16. feat: suspense guard in data and error getters

    Alternative to #168 Fixes #5

    This PR isolates the suspense guarding into a function, which is then called in the data and error getters returned from useSWR.

    This allows you to write your suspense useSWRs like this to avoid waterfalls;

    function App () {
      const user = useSWR('/api/user')
      const movies = useSWR('/api/movies')
    
      return (
        <div>
          Hi {user.data.name}, we have {movies.data.length} movies on the list.
        </div>
      )
    }
    
    // OR
    
    function App () {
      const userRequest = useSWR('/api/user')
      const moviesRequest = useSWR('/api/movies')
      const user = userRequest.data
      const movies = moviesRequest.data
    
      return (
        <div>
          Hi {user.name}, we have {movies.length} movies on the list.
        </div>
      )
    }
    

    This also allows for dependency fetching

    const user = useSWR('/api/user')
    const posts = useSWR('/api/posts')
    // Accessing .data will throw during key evaluation, letting swr know it's not ready
    const movies = useSWR(() => '/api/movies?id=' + user.data.id)
    

    And it would also (mostly) be backwards compatible, since most usage uses deconstruction which will automatically call the data and/or error getters, preserving earlier functionality.

    I believe this is a much cleaner interface than the other ones proposed. I've yet to test this completely, so it's mostly a proposal for now, feedback needed 😄

    Reviewed by h3rmanj at 2022-07-08 12:31
⚛️ Hooks for fetching, caching and updating asynchronous data in React
⚛️ Hooks for fetching, caching and updating asynchronous data in React

Hooks for fetching, caching and updating asynchronous data in React Enjoy this library? Try the entire TanStack! React Table, React Form, React Charts

Aug 6, 2022
Delightful data fetching for React.

Asynchronous dynamic data at scale. Performance, data integrity, and typing for REST, proto, GraphQL, websockets and more.

Jul 31, 2022
👩‍🍳 A React Hooks utility library containing popular customized hooks
👩‍🍳 A React Hooks utility library containing popular customized hooks

React Recipes A React Hooks utility library containing popular customized hooks What's your favorite dish? npm i react-recipes --save yarn add react-r

Jul 29, 2022
Async HTTP request data for axios. Designed for diverse UI states, SSR and data pre-caching.

React useApi() Axios-based React hooks for async HTTP request data. react-use-api feeds API data to React components when SSR (Server-Side Rendering),

Jul 15, 2022
React hooks and components for large data sets scrolling

React Scroller A set of hooks, components and utilities for scrolling large datasets. Standalone components could be used as well as lower level hooks

Mar 31, 2022
Simple and powerful API client for react. Use hooks to fetch data in easy way.

Simple and powerful API client for react. Use hooks to fetch data in easy way.

Apr 2, 2022
🍹 A lot of nice hooks to make react hooks easier to use ( useState callback / life cycle / instance variable)

?? Nice Hooks 中文版 A lot of nice hooks to make react hooks easier to use. If you find this project is useful to you, please give me a star. Installatio

May 5, 2022
🔥 A collection of beautiful and (hopefully) useful React hooks to speed-up your components and hooks development 🔥
🔥 A collection of beautiful and (hopefully) useful React hooks to speed-up your components and hooks development 🔥

A collection of beautiful (and hopefully useful) React hooks to speed-up your components and hooks development ?? Live playground here ?? ???? English

Aug 1, 2022
Learn the more advanced React hooks and different patterns to enable great developer APIs for custom hooks
Learn the more advanced React hooks and different patterns to enable great developer APIs for custom hooks

?? Advanced React Hooks ?? EpicReact.Dev Learn the more advanced React hooks and different patterns to enable great developer APIs for custom hooks. W

Mar 15, 2022
ReactJs Custom hooks, component lifecycle - Indispensable hooks

ReactJs Custom hooks, component lifecycle - Indispensable hooks

Jul 26, 2022
React Hooks Library
React Hooks Library

English | 简体中文 ahooks React Hooks Library. ?? Documentation English 中文 ✨ Features Easy to learn and use. Contains a wealth of advanced Hooks that are

Aug 4, 2022
React Hooks library for element visibility. Uses the intersection observer API.

react-hooks-visible react-hooks-visible is React Hooks library for element visibility. Uses the intersection observer API. demo Get started yarn add r

Jul 31, 2022
A hooks-based lightweight React library for state management

React async states A naive lightweight React library for managing state. What is this ? This is a library for decentralized state management in React.

Jul 16, 2022
Thistate is a library to manage global states with custom React hooks.

thistate · Thistate is a library to manage global states with custom React hooks. Documentation To use thistate follow the documentation to the some m

Mar 29, 2022
Turbulent - Blockchain interface hooks library for React.js and Next.js

Turbulent turbulent is a React.js/Next.js hook library for blockchain interface,

Jan 29, 2022
Haiku - A simple and lightweight React library that provides a large number of hooks and utilities

Haiku - a simple & lightweight React library with the goal of saving you time by offering a large collection of hooks & utilities

Aug 2, 2022
hooks-first CSS-in-JS library, focused on semantics and runtime performance
hooks-first CSS-in-JS library, focused on semantics and runtime performance

Trousers ?? React components are more stylish with Trousers! Try it here Trousers is a hooks-first CSS-in-JS library, designed to help developers auth

Jul 25, 2022
A simple Hooks wrapper over Motion One, An animation library, built on the Web Animations API for the smallest filesize and the fastest performance.

A simple Hooks wrapper over Motion One, An animation library, built on the Web Animations API for the smallest filesize and the fastest performance.

Jul 28, 2022