UNPKG

19.1 kBMarkdownView Raw
1[![SWR](https://assets.vercel.com/image/upload/v1572289618/swr/banner.png)](https://swr.vercel.app)
2
3<p align="center">
4
5 <a aria-label="Vercel logo" href="https://vercel.com">
6 <img src="https://badgen.net/badge/icon/Made%20by%20Vercel?icon=zeit&label&color=black&labelColor=black">
7 </a>
8 <br/>
9
10 <a aria-label="NPM version" href="https://www.npmjs.com/package/swr">
11 <img alt="" src="https://badgen.net/npm/v/swr">
12 </a>
13 <a aria-label="Package size" href="https://bundlephobia.com/result?p=swr">
14 <img alt="" src="https://badgen.net/bundlephobia/minzip/swr">
15 </a>
16 <a aria-label="License" href="https://github.com/zeit/swr/blob/master/LICENSE">
17 <img alt="" src="https://badgen.net/npm/license/swr">
18 </a>
19</p>
20
21## Introduction
22
23[swr.vercel.app](https://swr.vercel.app)
24
25SWR is a React Hooks library for remote data fetching.
26
27The name “**SWR**” is derived from `stale-while-revalidate`, a cache invalidation strategy popularized by [HTTP RFC 5861](https://tools.ietf.org/html/rfc5861).
28**SWR** first returns the data from cache (stale), then sends the fetch request (revalidate), and finally comes with the up-to-date data again.
29
30It features:
31
32- Transport and protocol agnostic data fetching
33- Fast page navigation
34- Revalidation on focus
35- Interval polling
36- Request deduplication
37- Local mutation
38- Pagination
39- TypeScript ready
40- SSR support
41- Suspense mode
42- React Native support
43- Minimal API
44
45...and a lot more.
46
47With SWR, components will get **a stream of data updates constantly and automatically**. Thus, the UI will be always **fast** and **reactive**.
48
49<br/>
50
51## Quick Start
52
53```js
54import useSWR from 'swr'
55
56function Profile() {
57 const { data, error } = useSWR('/api/user', fetcher)
58
59 if (error) return <div>failed to load</div>
60 if (!data) return <div>loading...</div>
61 return <div>hello {data.name}!</div>
62}
63```
64
65In this example, the React Hook `useSWR` accepts a `key` and a `fetcher` function.
66The `key` is a unique identifier of the request, normally the URL of the API. And the `fetcher` accepts
67`key` as its parameter and returns the data asynchronously.
68
69`useSWR` also returns 2 values: `data` and `error`. When the request (fetcher) is not yet finished,
70`data` will be `undefined`. And when we get a response, it sets `data` and `error` based on the result
71of `fetcher` and rerenders the component.
72
73Note that `fetcher` can be any asynchronous function, so you can use your favourite data-fetching
74library to handle that part.
75
76Check out [swr.vercel.app](https://swr.vercel.app) for more demos of SWR, and [Examples](#examples) for the best practices.
77
78<br/>
79
80## Usage
81
82Inside your React project directory, run the following:
83
84```
85yarn add swr
86```
87
88Or with npm:
89
90```
91npm install swr
92```
93
94### API
95
96```js
97const { data, error, isValidating, mutate } = useSWR(key, fetcher, options)
98```
99
100#### Parameters
101
102- `key`: a unique key string for the request (or a function / array / null) [(advanced usage)](#conditional-fetching)
103- `fetcher`: (_optional_) a Promise returning function to fetch your data [(details)](#data-fetching)
104- `options`: (_optional_) an object of options for this SWR hook
105
106#### Return Values
107
108- `data`: data for the given key resolved by `fetcher` (or undefined if not loaded)
109- `error`: error thrown by `fetcher` (or undefined)
110- `isValidating`: if there's a request or revalidation loading
111- `mutate(data?, shouldRevalidate?)`: function to mutate the cached data
112
113#### Options
114
115- `suspense = false`: enable React Suspense mode [(details)](#suspense-mode)
116- `fetcher = undefined`: the default fetcher function
117- `initialData`: initial data to be returned (note: This is per-hook)
118- `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)
119- `revalidateOnFocus = true`: auto revalidate when window gets focused
120- `revalidateOnReconnect = true`: automatically revalidate when the browser regains a network connection (via `navigator.onLine`)
121- `refreshInterval = 0`: polling interval (disabled by default)
122- `refreshWhenHidden = false`: polling when the window is invisible (if `refreshInterval` is enabled)
123- `refreshWhenOffline = false`: polling when the browser is offline (determined by `navigator.onLine`)
124- `shouldRetryOnError = true`: retry when fetcher has an error [(details)](#error-retries)
125- `dedupingInterval = 2000`: dedupe requests with the same key in this time span
126- `focusThrottleInterval = 5000`: only revalidate once during a time span
127- `loadingTimeout = 3000`: timeout to trigger the onLoadingSlow event
128- `errorRetryInterval = 5000`: error retry interval [(details)](#error-retries)
129- `errorRetryCount`: max error retry count [(details)](#error-retries)
130- `onLoadingSlow(key, config)`: callback function when a request takes too long to load (see `loadingTimeout`)
131- `onSuccess(data, key, config)`: callback function when a request finishes successfully
132- `onError(err, key, config)`: callback function when a request returns an error
133- `onErrorRetry(err, key, config, revalidate, revalidateOps)`: handler for [error retry](#error-retries)
134- `compare(a, b)`: comparison function used to detect when returned data has changed, to avoid spurious rerenders. By default, [`dequal/lite`](https://github.com/lukeed/dequal) is used.
135
136When under a slow network (2G, <= 70Kbps), `errorRetryInterval` will be 10s, and
137`loadingTimeout` will be 5s by default.
138
139You can also use a [global configuration](#global-configuration) to provide default options.
140
141<br/>
142
143## Examples
144
145- [Global Configuration](#global-configuration)
146- [Data Fetching](#data-fetching)
147- [Conditional Fetching](#conditional-fetching)
148- [Dependent Fetching](#dependent-fetching)
149- [Multiple Arguments](#multiple-arguments)
150- [Manually Revalidate](#manually-revalidate)
151- [Mutation and Post Request](#mutation-and-post-request)
152- [Mutate Based on Current Data](#mutate-based-on-current-data)
153- [Returned Data from Mutate](#returned-data-from-mutate)
154- [SSR with Next.js](#ssr-with-nextjs)
155- [Suspense Mode](#suspense-mode)
156- [Error Retries](#error-retries)
157- [Prefetching Data](#prefetching-data)
158- [Request Deduplication](#request-deduplication)
159
160### Global Configuration
161
162The context `SWRConfig` can provide global configurations (`options`) for all SWR hooks.
163
164In this example, all SWRs will use the same fetcher provided to load JSON data, and refresh every 3 seconds by default:
165
166```js
167import useSWR, { SWRConfig } from 'swr'
168
169function Dashboard() {
170 const { data: events } = useSWR('/api/events')
171 const { data: projects } = useSWR('/api/projects')
172 const { data: user } = useSWR('/api/user', { refreshInterval: 0 }) // don't refresh
173 // ...
174}
175
176function App() {
177 return (
178 <SWRConfig
179 value={{
180 refreshInterval: 3000,
181 fetcher: (...args) => fetch(...args).then(res => res.json())
182 }}
183 >
184 <Dashboard />
185 </SWRConfig>
186 )
187}
188```
189
190### Data Fetching
191
192`fetcher` is a function that **accepts the `key`** of SWR, and returns a value or a Promise.
193You can use any library to handle data fetching, for example:
194
195```js
196import fetch from 'unfetch'
197
198const fetcher = url => fetch(url).then(r => r.json())
199
200function App() {
201 const { data } = useSWR('/api/data', fetcher)
202 // ...
203}
204```
205
206Or using GraphQL:
207
208```js
209import { request } from 'graphql-request'
210
211const API = 'https://api.graph.cool/simple/v1/movies'
212const fetcher = query => request(API, query)
213
214function App() {
215 const { data, error } = useSWR(
216 `{
217 Movie(title: "Inception") {
218 releaseDate
219 actors {
220 name
221 }
222 }
223 }`,
224 fetcher
225 )
226 // ...
227}
228```
229
230_If you want to pass variables to a GraphQL query, check out [Multiple Arguments](#multiple-arguments)._
231
232Note that `fetcher` can be omitted from the parameters if it's provided globally.
233
234### Conditional Fetching
235
236Use `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.
237
238```js
239// conditionally fetch
240const { data } = useSWR(shouldFetch ? '/api/data' : null, fetcher)
241
242// ...or return a falsy value
243const { data } = useSWR(() => shouldFetch ? '/api/data' : null, fetcher)
244
245// ... or throw an error when user.id is not defined
246const { data } = useSWR(() => '/api/data?uid=' + user.id, fetcher)
247```
248
249### Dependent Fetching
250
251SWR 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.
252
253```js
254function MyProjects() {
255 const { data: user } = useSWR('/api/user')
256 const { data: projects } = useSWR(() => '/api/projects?uid=' + user.id)
257 // When passing a function, SWR will use the return
258 // value as `key`. If the function throws or returns
259 // falsy, SWR will know that some dependencies are not
260 // ready. In this case `user.id` throws when `user`
261 // isn't loaded.
262
263 if (!projects) return 'loading...'
264 return 'You have ' + projects.length + ' projects'
265}
266```
267
268### Multiple Arguments
269
270In some scenarios, it's useful to pass multiple arguments (can be any value or object) to the `fetcher` function. For example:
271
272```js
273useSWR('/api/user', url => fetchWithToken(url, token))
274```
275
276This is **incorrect**. Because the identifier (also the index of the cache) of the data is `'/api/user'`,
277so even if `token` changes, SWR will still have the same key and return the wrong data.
278
279Instead, you can use an **array** as the `key` parameter, which contains multiple arguments of `fetcher`:
280
281```js
282const { data: user } = useSWR(['/api/user', token], fetchWithToken)
283
284// ...and pass it as an argument to another query
285const { data: orders } = useSWR(user ? ['/api/orders', user] : null, fetchWithUser)
286```
287
288The key of the request is now the combination of both values. SWR **shallowly** compares
289the arguments on every render and triggers revalidation if any of them has changed.
290Keep in mind that you should not recreate objects when rendering, as they will be treated as different objects on every render:
291
292```js
293// Don’t do this! Deps will be changed on every render.
294useSWR(['/api/user', { id }], query)
295
296// Instead, you should only pass “stable” values.
297useSWR(['/api/user', id], (url, id) => query(url, { id }))
298```
299
300Dan Abramov explains dependencies very well in [this blog post](https://overreacted.io/a-complete-guide-to-useeffect/#but-i-cant-put-this-function-inside-an-effect).
301
302### Manually Revalidate
303
304You can broadcast a revalidation message globally to all SWRs with the same key by calling
305`mutate(key)`.
306
307This example shows how to automatically refetch the login info (e.g.: inside `<Profile/>`)
308when the user clicks the “Logout” button.
309
310```js
311import useSWR, { mutate } from 'swr'
312
313function App() {
314 return (
315 <div>
316 <Profile />
317 <button onClick={() => {
318 // set the cookie as expired
319 document.cookie = 'token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;'
320
321 // tell all SWRs with this key to revalidate
322 mutate('/api/user')
323 }}>
324 Logout
325 </button>
326 </div>
327 )
328}
329```
330
331### Mutation and Post Request
332
333In many cases, applying local mutations to data is a good way to make changes
334feel faster — no need to wait for the remote source of data.
335
336With `mutate`, you can update your local data programmatically, while
337revalidating and finally replace it with the latest data.
338
339```js
340import useSWR, { mutate } from 'swr'
341
342function Profile() {
343 const { data } = useSWR('/api/user', fetcher)
344
345 return (
346 <div>
347 <h1>My name is {data.name}.</h1>
348 <button onClick={async () => {
349 const newName = data.name.toUpperCase()
350 // send a request to the API to update the data
351 await requestUpdateUsername(newName)
352 // update the local data immediately and revalidate (refetch)
353 // NOTE: key has to be passed to mutate as it's not bound
354 mutate('/api/user', { ...data, name: newName })
355 }}>Uppercase my name!</button>
356 </div>
357 )
358}
359```
360
361Clicking the button in the example above will send a POST request to modify the remote data, locally update the client data and
362try to fetch the latest one (revalidate).
363
364But many POST APIs will just return the updated data directly, so we don’t need to revalidate again.
365Here’s an example showing the “local mutate - request - update” usage:
366
367```js
368mutate('/api/user', newUser, false) // use `false` to mutate without revalidation
369mutate('/api/user', updateUser(newUser)) // `updateUser` is a Promise of the request,
370 // which returns the updated document
371```
372
373### Mutate Based on Current Data
374
375In many cases, you are receiving a single value back from your API and want to update a list of them.
376
377With `mutate`, you can pass an async function which will receive the current cached value, if any, and let you return an updated document.
378
379```js
380mutate('/api/users', async users => {
381 const user = await fetcher('/api/users/1')
382 return [user, ...users.slice(1)]
383})
384```
385
386### Returned Data from Mutate
387
388Most 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`.
389
390The 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.
391
392```js
393try {
394 const user = await mutate('/api/user', updateUser(newUser))
395} catch (error) {
396 // Handle an error while updating the user here
397}
398```
399
400### Bound `mutate()`
401
402The SWR object returned by `useSWR` also contains a `mutate()` function that is pre-bound to the SWR's key.
403
404It is functionally equivalent to the global `mutate` function but does not require the `key` parameter.
405
406```js
407import useSWR from 'swr'
408
409function Profile() {
410 const { data, mutate } = useSWR('/api/user', fetcher)
411
412 return (
413 <div>
414 <h1>My name is {data.name}.</h1>
415 <button onClick={async () => {
416 const newName = data.name.toUpperCase()
417 // send a request to the API to update the data
418 await requestUpdateUsername(newName)
419 // update the local data immediately and revalidate (refetch)
420 // NOTE: key is not required when using useSWR's mutate as it's pre-bound
421 mutate({ ...data, name: newName })
422 }}>Uppercase my name!</button>
423 </div>
424 )
425}
426```
427
428### SSR with Next.js
429
430With the `initialData` option, you pass an initial value to the hook. It works perfectly with many SSR solutions
431such as `getServerSideProps` in [Next.js](https://github.com/zeit/next.js):
432
433```js
434export async function getServerSideProps() {
435 const data = await fetcher('/api/data')
436 return { props: { data } }
437}
438
439function App(props) {
440 const initialData = props.data
441 const { data } = useSWR('/api/data', fetcher, { initialData })
442
443 return <div>{data}</div>
444}
445```
446
447It is still a server-side rendered site, but it’s also fully powered by SWR in the client-side.
448Which means the data can be dynamic and update itself over time and user interactions.
449
450### Suspense Mode
451
452You can enable the `suspense` option to use SWR with React Suspense:
453
454```js
455import { Suspense } from 'react'
456import useSWR from 'swr'
457
458function Profile() {
459 const { data } = useSWR('/api/user', fetcher, { suspense: true })
460 return <div>hello, {data.name}</div>
461}
462
463function App() {
464 return (
465 <Suspense fallback={<div>loading...</div>}>
466 <Profile/>
467 </Suspense>
468 )
469}
470```
471
472In Suspense mode, `data` is always the fetch response (so you don't need to check if it's `undefined`).
473But if an error occurred, you need to use an [error boundary](https://reactjs.org/docs/concurrent-mode-suspense.html#handling-errors) to catch it.
474
475_Note that Suspense is not supported in SSR mode._
476
477### Error Retries
478
479By default, SWR uses the [exponential backoff algorithm](https://en.wikipedia.org/wiki/Exponential_backoff) to handle error retries.
480You can read more from the source code.
481
482It's also possible to override the behavior:
483
484```js
485useSWR(key, fetcher, {
486 onErrorRetry: (error, key, option, revalidate, { retryCount }) => {
487 if (retryCount >= 10) return
488 if (error.status === 404) return
489
490 // retry after 5 seconds
491 setTimeout(() => revalidate({ retryCount: retryCount + 1 }), 5000)
492 }
493})
494```
495
496### Prefetching Data
497
498There’re many ways to prefetch the data for SWR. For top-level requests, [`rel="preload"`](https://developer.mozilla.org/en-US/docs/Web/HTML/Preloading_content) is highly recommended:
499
500```html
501<link rel="preload" href="/api/data" as="fetch" crossorigin="anonymous">
502```
503
504This will prefetch the data before the JavaScript starts downloading. And your incoming fetch requests will reuse the result (including SWR, of course).
505
506Another choice is to prefetch the data conditionally. You can have a function to refetch and set the cache:
507
508```js
509function prefetch() {
510 mutate('/api/data', fetch('/api/data').then(res => res.json()))
511 // the second parameter is a Promise
512 // SWR will use the result when it resolves
513}
514```
515
516And use it when you need to preload the **resources** (for example when [hovering](https://github.com/GoogleChromeLabs/quicklink) [a](https://github.com/guess-js/guess) [link](https://instant.page)).
517Together with techniques like [page prefetching](https://nextjs.org/docs#prefetching-pages) in Next.js, you will be able to load both next page and data instantly.
518
519### Request Deduplication
520
521SWR 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.
522Here, the 'api/user' key is used in two requests:
523
524```js
525import useSWR from 'swr'
526
527function UserProfileName() {
528 const { data, error } = useSWR('/api/user', fetcher)
529
530 if (error) return <div>failed to load</div>
531 if (!data) return <div>loading...</div>
532 return <p>Name: {data.name}!</p>
533}
534
535function UserProfileAvatar() {
536 const { data, error } = useSWR('/api/user', fetcher)
537
538 if (error) return <div>failed to load</div>
539 if (!data) return <div>loading...</div>
540 return <img src={data.avatarUrl} alt="Profile image" />
541}
542
543export default function App() {
544 return (
545 <div>
546 <UserProfileName />
547 <UserProfileAvatar />
548 </div>
549 )
550}
551```
552
553By default, requests made within 2 seconds are deduped. This can be changed by setting the `dedupingInterval` option:
554
555```js
556const { data, error } = useSWR('/api/user', fetcher, { dedupingInterval: 1000 })
557```
558
559This will deduplicate requests at an interval of 1 second.
560<br/>
561
562## Authors
563
564- Shu Ding ([@shuding_](https://twitter.com/shuding_)) – [Vercel](https://vercel.com)
565- Guillermo Rauch ([@rauchg](https://twitter.com/rauchg)) – [Vercel](https://vercel.com)
566- Joe Haddad ([@timer150](https://twitter.com/timer150)) - [Vercel](https://vercel.com)
567- Paco Coursey ([@pacocoursey](https://twitter.com/pacocoursey)) - [Vercel](https://vercel.com)
568
569Thanks to Ryan Chen for providing the awesome `swr` npm package name!
570
571<br/>
572
573## License
574
575The MIT License.