diff --git a/README.md b/README.md index 98ab7c019..f2ef10c10 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,5 @@ [![SWR](https://assets.zeit.co/image/upload/v1572289618/swr/banner.png)](https://swr.now.sh) -

- swr.now.sh -

-

@@ -19,11 +15,13 @@

-## Intro +## Introduction + +[swr.now.sh](https://swr.now.sh) SWR is a React Hooks library for remote data fetching. -The name “**SWR**” is derived from `stale-while-revalidate`, a HTTP cache invalidation strategy popularized by [RFC 5861](https://tools.ietf.org/html/rfc5861). +The name “**SWR**” is derived from `stale-while-revalidate`, a HTTP cache invalidation strategy popularized by [RFC 5861](https://tools.ietf.org/html/rfc5861). **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: @@ -37,11 +35,11 @@ It features: - Suspense mode - Minimal API -With SWR, components will get a stream of data updates constantly and automatically, Thus, the UI will be always fast and reactive. +...and a lot more. -## Quick Start +With SWR, components will get **a stream of data updates constantly and automatically**. Thus, the UI will be always **fast** and **reactive**. -To install, run `yarn add swr` or `npm install swr` in your React project. +## Quick Start ```js import useSWR from 'swr' @@ -56,7 +54,7 @@ function Profile () { ``` In this example, the React Hook `useSWR` accepts a `key` and a `fetcher` function. -`key` is a unique identifier of the data, normally a URL of the API. And the `fetcher` accepts +`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, @@ -66,209 +64,276 @@ 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.now.sh](https://swr.now.sh) for more demos of SWR. -- API - - [`useSWR`](#useswr) - - [`SWRConfig`](#swrconfig) - - [`mutate`](#mutate) - - [`trigger`](#trigger) -- Examples - - [Suspense Mode](#suspense-mode) - - [Subscription (e.g.: socket.io)](#subscription-eg-socketio) - - [Dependent Fetching](#dependent-fetching) +## Usage -## API +Inside your React project directory, run the following: -### `useSWR` +``` +yarn add swr +``` + +Or with npm: + +``` +npm install swr +``` + +### API ```js -const { - data, // data for the given key (or undefined) - error, // error (or undefined) - isValidating, // if the request is loading - revalidate // function to trigger a validate manually -} = useSWR( - key, // a unique key for the data (or a function, see below) - fetcher, // Promise returning function to load your data - swrOptions? = { - suspense: false, // enabled React Suspense mode - revalidateOnFocus: true, // auto revalidate when window gets focused - refreshWhenHidden: false, // refresh while the window is invisible - shouldRetryOnError: true, // retry when fetcher has an error - refreshInterval: 0, // polling interval (disabled by default) - errorRetryInterval: 5000, // error retry interval (10s on slow network) - focusThrottleInterval: 5000, // keep focus revalidate requests in a time window - dedupingInterval: 2000, // deduping requests - loadingTimeout: 3000, // timeout for triggering the onLoadingSlow event - - onLoadingSlow, // event handlers - onSuccess, - onError, - onErrorRetry, - - fetcher // default fetcher function - } -) +const { data, error, isValidating, revalidate } = useSWR(key, fetcher, options) ``` -#### `key` as a function +#### Parameters + +- `key`: a unique key string for the request (or a function / null) [(advanced usage)](#conditional-fetching) +- `fetcher`: (_optional_) a Promise returning function to fetch your data [(details)](#data-fetching) +- `options`: (_optional_) an object of options for this SWR hook -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. +#### 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 +- `revalidate`: function to trigger the validation manually + +#### Options + +- `suspense = false`: enable React Suspense mode [(details)](#suspense-mode) +- `fetcher = undefined`: the default fetcher function +- `revalidateOnFocus = true`: auto revalidate when window gets focused +- `refreshInterval = 0`: polling interval (disabled by default) +- `refreshWhenHidden = false`: polling when the window is invisible (if `refreshInterval` is enabled) +- `shouldRetryOnError = true`: retry when fetcher has an error [(details)](#error-retries) +- `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)](#error-retries) +- `onLoadingSlow`: callback function when a request takes too long to load (`loadingTimeout`) +- `onSuccess`: callback function when a request finishs successfully +- `onError`: callback function when a request returns an error +- `onErrorRetry`: handler for [error retry](#error-retries) + +When under a slow network (2G, <= 70Kbps), `errorRetryInterval` will be 10s, and +`loadingTimeout` will be 5s by default. + +You can also use [global configuration](#global-configuration) to provide default options. + +## Examples + +- [Global Configuration](#global-configuration) +- [Data Fetching](#data-fetching) +- [Conditional Fetching](#conditional-fetching) +- [Dependent Fetching](#dependent-fetching) +- [Manually Revalidate](#manually-revalidate) +- [Local Mutation](#local-mutation) +- [Suspense Mode](#suspense-mode) +- [Error Retries](#error-retries) + +### Global Configuration + +You can use `SWRConfig` to provide global configurations (`options`) for all SWR hooks. + +In this example, all `useSWR` hooks will use the same fetcher provided to load JSON data, and refresh every 3 seconds (except the user API): ```js -// key returns a falsy value -const { data } = useSWR(() => shouldFetch ? '/api/data' : null, fetcher) +import useSWR, { SWRConfig } from 'swr' -// key throws an error when user.id is not defined -const { data } = useSWR(() => '/api/data?uid=' + user.id, fetcher) +function Dashboard () { + const { data: events } = useSWR('/api/events') + const { data: projects } = useSWR('/api/projects') + const { data: user } = useSWR('/api/user', { refreshInterval: 0 }) + // ... +} + +function App () { + return ( + fetch(...args).then(res => res.json()) + }} + > + + + ) +} ``` -### `SWRConfig` +### Data Fetching -A context to provide global configurations (`swrOptions`) for SWR. +`fetcher` is a function **accepts the `key`** of SWR, and returns a value or a Promise. +You can use any library you to handle data fetching, for example: ```js -import useSWR, { SWRConfig } from 'swr' +import fetch from 'unfetch' + +const fetcher = url => fetch(url).then(r => r.json()) function App () { - // all the SWRs inside will use `refreshInterval: 1000` - // and the native `fetch` implementation - return fetch(...args).then(res => res.json()) - }}> - - + const { data } = useSWR('/api/data', fetcher) + // ... } +``` -function Profile () { - const { data, error } = useSWR('/api/user') +Or using GraphQL: +```js +import { request } from 'graphql-request' + +const API = 'https://api.graph.cool/simple/v1/movies' +const fetcher = query => request(API, query) + +function App () { + const { data, error } = useSWR( + `{ + Movie(title: "Inception") { + releaseDate + actors { + name + } + } + }`, + fetcher + ) // ... } ``` -### `mutate` +Note that `fetcher` can be skipped from the parameters if it's provided gloablly. -With `mutate`, you can update your local data programmatically, while -revalidating and finally replace it. +### 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. ```js -import useSWR, { mutate } from 'swr' +// conditionally fetch +const { data } = useSWR(shouldFetch ? '/api/data' : null, fetcher) -function Profile () { - const { data } = useSWR('/api/user', 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. + +```js +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, + // SWR will know that some dependencies are not + // ready. In this case it is `user`. - return
-

My name is {data.name}.

- -
+ if (!projects) return 'loading...' + return 'You have ' + projects.length + ' projects' } ``` -### `trigger` +### Manually Revalidate You can broadcast a revalidation message to all SWR data inside any component by calling `trigger(key)`. +This example shows how to automatically refetch the login info (e.g.: inside ``) +when the user clicks the “Logout” button. + ```js import useSWR, { trigger } from 'swr' function App () { - return
- - -
+ return ( +
+ + +
+ ) } ``` -## Examples +### Local Mutation -### Suspense Mode +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. -You can enable the `suspense` option to use `useSWR` with React Suspense. +With `mutate`, you can update your local data programmatically, while +revalidating and finally replace it with the latest data. ```js -import { Suspense } from 'react' -import useSWR from 'swr' +import useSWR, { mutate } from 'swr' function Profile () { - const { data } = useSWR('/api/user', fetcher, { suspense: true }) - return
hello, {data.name}
-} + const { data } = useSWR('/api/user', fetcher) -function App () { - return loading...}> - - + return ( +
+

My name is {data.name}.

+ +
+ ) } ``` -### Subscription (e.g.: socket.io) +### Suspense Mode -You can use SWR with socket.io (generally any subscription pattern) like this: +You can enable the `suspense` option to use SWR with React Suspense: ```js -// fetch-data.js - -import { mutate } from 'swr' - -let latestData = null +import { Suspense } from 'react' +import useSWR from 'swr' -// setup ws and broadcast to all SWRs -... -socket.on('data', data => { - latestData = data - mutate('/api/data', data, false) -}) +function Profile () { + const { data } = useSWR('/api/user', fetcher, { suspense: true }) + return
hello, {data.name}
+} -export default () => latestData +function App () { + return ( + loading...}> + + + ) +} ``` -and your component: +Note in Suspense mode, `data` is always the fetch response (so you don't need to check if it's `undefined`). But if there's an error occurred, you need to use an [error boundary](https://reactjs.org/docs/concurrent-mode-suspense.html#handling-errors) to catch it. -```js -import useSWR from 'swr' -import fetchData from './fetch-data' +### Error Retries -function App () { - const { data } = useSWR('/api/data', fetchData) - // ... -} -``` +By default, SWR uses the [exponential backoff algorithm](https://en.wikipedia.org/wiki/Exponential_backoff) to handle error retries. +You can read more from the source code. -### Dependent Fetching -SWR 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. +It's also possible to override the behavior: ```js -import useSWR from 'swr' - -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, - // SWR will know that some dependencies are not - // ready. In this case it is `user`. +useSWR(key, fetcher, { + onErrorRetry: (error, key, option, revalidate, { retryCount }) => { + if (retryCount >= 10) return + if (error.status === 404) return - if (!projects) return 'loading...' - return 'You have ' + projects.length + ' projects' -} + // retry after 5 seconds + setTimeout(() => revalidate({ retryCount: retryCount + 1 }), 5000) + } +}) ``` ## Authors