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.