Getting Started
Installation
Inside your React project directory, run the following:
pnpm add swr
Quick Start
For normal RESTful APIs with JSON data, first you need to create a fetcher
function, which is just a wrapper of the native fetch
:
const fetcher = (...args) => fetch(...args).then(res => res.json())
If you want to use GraphQL API or libs like Axios, you can create your own fetcher function. Check here for more examples.
Then you can import useSWR
and start using it inside any function components:
import useSWR from 'swr'
function Profile () {
const { data, error, isLoading } = useSWR('/api/user/123', fetcher)
if (error) return <div>failed to load</div>
if (isLoading) return <div>loading...</div>
// render data
return <div>hello {data.name}!</div>
}
Normally, there're 3 possible states of a request: "loading", "ready", or "error". You can use the value of data
, error
and isLoading
to
determine the current state of the request, and return the corresponding UI.
Make It Reusable
When building a web app, you might need to reuse the data in many places of the UI. It is incredibly easy to create reusable data hooks on top of SWR:
function useUser (id) {
const { data, error, isLoading } = useSWR(`/api/user/${id}`, fetcher)
return {
user: data,
isLoading,
isError: error
}
}
And use it in your components:
function Avatar ({ id }) {
const { user, isLoading, isError } = useUser(id)
if (isLoading) return <Spinner />
if (isError) return <Error />
return <img src={user.avatar} />
}
By adopting this pattern, you can forget about fetching data in the imperative way: start the request, update the loading state, and return the final result. Instead, your code is more declarative: you just need to specify what data is used by the component.
Example
In a real-world example, our website shows a navbar and the content, both depend on user
:
Traditionally, we fetch data once using useEffect
in the top level component, and pass it to child components via props (notice that we don't handle error state for now):
// page component
function Page () {
const [user, setUser] = useState(null)
// fetch data
useEffect(() => {
fetch('/api/user')
.then(res => res.json())
.then(data => setUser(data))
}, [])
// global loading state
if (!user) return <Spinner/>
return <div>
<Navbar user={user} />
<Content user={user} />
</div>
}
// child components
function Navbar ({ user }) {
return <div>
...
<Avatar user={user} />
</div>
}
function Content ({ user }) {
return <h1>Welcome back, {user.name}</h1>
}
function Avatar ({ user }) {
return <img src={user.avatar} alt={user.name} />
}
Usually, we need to keep all the data fetching in the top level component and add props to every component deep down the tree. The code will become harder to maintain if we add more data dependency to the page.
Although we can avoid passing props using Context (opens in a new tab), there's still the dynamic content problem: components inside the page content can be dynamic, and the top level component might not know what data will be needed by its child components.
SWR solves the problem perfectly. With the useUser
hook we just created, the code can be refactored to:
// page component
function Page () {
return <div>
<Navbar />
<Content />
</div>
}
// child components
function Navbar () {
return <div>
...
<Avatar />
</div>
}
function Content () {
const { user, isLoading } = useUser()
if (isLoading) return <Spinner />
return <h1>Welcome back, {user.name}</h1>
}
function Avatar () {
const { user, isLoading } = useUser()
if (isLoading) return <Spinner />
return <img src={user.avatar} alt={user.name} />
}
Data is now bound to the components which need the data, and all components are independent to each other. All the parent components don't need to know anything about the data or passing data around. They just render. The code is much simpler and easier to maintain now.
The most beautiful thing is that there will be only 1 request sent to the API, because they use the same SWR key and the request is deduped, cached and shared automatically.
Also, the application now has the ability to refetch the data on user focus or network reconnect! That means, when the user's laptop wakes from sleep or they switch between browser tabs, the data will be refreshed automatically.