GitHub: github.com/zeit/swr

SWR: React Hooks for Remote Data Fetching

introduce

SWR is a React Hooks library that provides remote data requests.

SWR, consisting of stables -while-revalidate, is a cache invalidation policy popularized by HTTP RFC 5861. SWR returns cached data first (stable), then sends a remote request (revalidate), and finally updates the latest data.

Features:

  • Data requests are transport protocol independent
  • Fast page navigation
  • Data is automatically requested while focusing
  • polling
  • Request to heavy
  • Local data changes
  • paging
  • Support for TS
  • Support the SSR
  • Support the React Suspense
  • Support the React Native
  • A few apis, etc…….

With SWR, components can continuously update data automatically. So the UI is always fast and responsive.

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>
}
Copy the code

In this example, useSWR accepts the key and fetcher as parameters. The key is the unique identifier of the request, usually the URL of the API. The fetcher function takes the key as an argument and returns data asynchronously.

UseSWR returns two values: data and error. When the request is in progress, the value of data is undefined. When the request ends and a response is returned, data and ERROR are set based on the fetcher return value and the component is rerendered.

Note that fetcher can be any asynchronous function, so you can use your favorite data request library to handle this part.

See more examples of SWR example best practices

Begin to use

Go to your React project and execute the following command

yarn add swr

Or use NPM

npm install swr

API

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

parameter

  • Key: string | function | array | null, unique advanced usage of the request
  • Fetcher: An optional argument that returns a Promise function used to request remote data details
  • Options: object, optional parameter, SWR hook receive option object

The return value

  • Data: The Promise returned by fetcher, resolve (undefined if the request is not returned)
  • Error: error (or undefined) thrown by the fetcher function
  • IsValidating: True As Loading state (the request is in process or data is being valid again)
  • mutate: (data? : any, shouldRevalidate? : any) => void, the function used to change the cached data

options

  • Suspense = false: Whether to enable React Suspense mode details
  • Fetcher = undefined: the default fetcher function
  • InitialData: Default data returned (note: this is the default hook)
  • RevalidateOnFocus = true: Automatically obtains data when focusing a window
  • RevalidateOnReconnect = true: Automatically retrieve data when browser reconnects to the network (via: navigator.online)
  • RefreshInterval = 0: Polling interval (polling is not enabled by default)
  • RefreshWhenHidden = false: Polling continues when the window is not visible (when refreshInterval is enabled)
  • RefreshWhenOffline = true: Polling continues when the browser is offline (as determined by navigator.online)
  • ShouldRetryOnError = true: Fetcher whether to retry the details when an error occurs
  • DedupingInterval = 2000: anti-shake, frequent start revalidate, the anti-shake policy processing
  • FocusThrottleInterval = 5000: Revalidate is executed only once during this interval
  • LoadingTimeout = 3000: Timeout period for triggering onLoadingSlow events. That is, the onLoadingSlow callback is triggered if the request is not completed within the specified time
  • ErrorRetryInterval = 5000: errorRetryInterval
  • ErrorRetryCount: Indicates the maximum number of error retries
  • OnLoadingSlow (key, config) : callback function that triggers if the request takes too long (see loadingTimeout)
  • OnSuccess (data, key, config) : callback function if the request succeeds
  • OnError (err, key, config) : callback function when error is returned
  • OnErrorRetry (Err, key, config, revalidate, revalidateOps) : Error retry handler details
  • Compare (a,b) : a function used to compare whether the returned data has actually changed to avoid fraudulent rerendering. By default, fast-deep-equal is used

When the network is slow (2G, <=70kbps), the default errorRetryInterval is 10 seconds, and the default loadingTimeout is 5 seconds

Default option values can be provided through global configuration.

example

Global Configuration

SWRConfig can provide global default configuration.

In the following example, all SWRS will use the same fetcher to request JSON data and refresh the data every 3s:

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 }) // Disable refresh
}

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

Data Fetching

Fetcher is a function that takes a key as an argument and returns a value or Promise. You can use any library to handle data requests, for example:

import fetch from 'unfetch'

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

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

Or use GraphQL:

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
  )
  // ...
}
Copy the code

If you want to pass variables to the GraphQL Query, look here

Note that the fetcher parameter can be omitted from useSWR if global fetcher is provided

Conditional Fetching

Pass EITHER NULL or a function to useSWR as a key to initiate data requests based on conditions. If the function throws an error or returns false, SWR aborts the request.

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

/ / returns false
const { data } = useSWR((a)= > shouldFetch ? '/api/data' : null, fethcer)

// Throw an error
const { data } = useSWR((a)= > '/api/data? uid=' + user.id, fetcher)
Copy the code

Dependent Fetching

SWR allows your remote request data to depend on other data. It maximizes parallelism (avoiding waterfall development) while ensuring that the next data request requests dynamic data.

function MyProjects() {
	const { data: user } = useSWR('/api/user')
	const { data: projects } = useSWR((a)= > '/api/projects? uid=' + user.id)
	/* When the key passed to SWR is a function, the return value of this function is the value of key. If this function throws an error, SWR knows that some dependencies are not ready. In this case, user. * /
	if(! projects)return 'loading'
	return 'You have ' + projects.length + 'projects'
}
Copy the code

Multiple Arguments

In some scenarios, it is useful to pass multiple arguments (which can be any value or object) to the fetcher function. Such as:

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

This is not true. Because the unique identifier of the data (cached index) is “/ API /data”, even if the token changes, SWR will still use the same key and return the wrong data.

You should use an array as the key argument, which is made up of the arguments fetcher receives:

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

const { data: orders } = useSWR(user ? ['/api/orders', user] : null, fetchWithUser)
Copy the code

The requested key value is now associated with all value changes. On each render, SWR makes a shallow comparison of the parameters and triggers revalidation if anything changes.

Remember, when rendering, you should not recreate the object, or it will be treated as a different object each time:

// Do not write this, each render will be treated as a change
useSWR(['/api/user', { id }], query)

// Stable values should be passed in
useSWR(['/api/user', id], (url, id) => query(user, { id }))
Copy the code

Dan Abramov explains it well here.

Manually Revalidate

Call mutate(key) to broadcast a revalidation message to all SWRS that use the same key.

This example shows an implementation of automatically retrieving login information (LLDB: internal) when the user clicks the “Exit” button.

import useSWR, { mutate } from 'swr'

function App() {
	return (
		<div>
			<Profile />
			<button
				onClick = (()= >{// Set cookie expiration document.cookie = 'token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; '/ / publish broadcast, all SWRS with the same key value retrieve data mutate('/ API /user'); }) > Logout</button>
		</div>)}Copy the code

Mutation and Post Request (Mutation and Post Request)

In many cases, modifying local data first can feel like a faster response — no need to wait for remote data.

With Mutate, you can update your local data first and then replace it with the latest data when the request ends.

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 () {const newName = data.name.toupperCase () {const newName = data.name.toupperCase (); Pass the key to mutate without limiting mutate('/ API /user', {... data, name: newName }) }} > Uppercase my name!</button>
		</div>)}Copy the code

Clicking the button in the above example sends a POST request to modify remote data, update client data locally, and attempt to request the latest data.

However, many of the POST APIs simply return updated data, so there is no need to revalidate again. The following example shows how to use local mutate-request-update:

// false to disable revalidation
	mutate('/api/user', newUser, false)
// updateUser is a request Promise that returns an updated document
	mutate('/api/user', updateUser(newUser))
Copy the code

Mutate Based on Current Data

In many scenarios, the API returns a single piece of data that you need to append to the list.

With mutate, you can pass in an asynchronous function that accepts the current cached value, and you can return an updated document.

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

Returned Data from Mutate

Perhaps, when you pass a promise or an async function to mutate, you need to update the cache with the return value.

Each time the following function is called, an updated document is returned or an error is thrown.

try {
	const user = await mutate('/api/user', updateUser(newUser))
} catch (error) {
	// Handle an error thrown when updating the user
}
Copy the code

Bound mutate()

The SWR object returned by useSWR also contains the mutate function, which prerestricts the key to the one passed in.

Functions the same as global mutate, but without passing in the key argument:

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() await requestUpdateUsername(newName) mutate({ ... data, name: newName }) }}> UpperCase my name!</button>
		</div>)}Copy the code

Implementing SSR with next.js

Using the initialData option, you can pass in an initial value to the hook. It works well in many SSR solutions, such as getServerSideProps in next.js:

export asyn 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>
}
Copy the code

In the above example, while maintaining SSR, SWR can also be well applied in the client. This means that data can be dynamic and constantly updated over time and user interaction.

Suspense Mode

In Suspense, you can turn on the Suspense option:

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

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>)}Copy the code

In Suspense mode, data is used to request data for responses (so you don’t need to check whether the data is undefined). However, if an error occurs, you need to use error Boundary to catch the error.

Suspense/ note that Suspense/ is not supported in SSR mode

Error Retires

SWR default uses exponential backoff algorithm to handle error retry. You can read the source code to learn more. It can also be overridden with onErrorRetry:

useSWR(key, fetcher, {
	onErrorRetry: (error, key, option, revalidation, { retryCount }) = > {
		if (retryCount >= 10) return
		if (error.status === 404) return
		
		// Try again after 5 seconds
		setTimeout((a)= > revalidation({ retryCount: retryCount + 1}), 5000)}})Copy the code

Preloaded Data

There are many ways to implement preloading in SWR. For top-level requests, rel = “preload” is highly recommended:

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

This approach starts preloading the data before the JS starts to download. This result can therefore be reused by subsequent requests (including the SWR, of course).

Another option is conditional preloading. You can write a function to rerequest and set the cache:

function prefetch() {
	mutate('/api/data', fetch('/api/data').then(res= > res.json()))
// The second argument is Promise
SWR will use this result when the Promise resolve is resolved
}
Copy the code

You can also use them when you need to reload resources (such as Hovering a link). Used with page preload in next.js, it loads the Next page and data immediately.