Why “useSwr” (stale-while-revalidate)?
- There are only two hard things in Computer Science: cache invalidation and naming things — Phil Karlton
- It fetches data from an API or other external source, then saves that data in a cache, and renders the data
- Allows us to load cached content right away
- At the same time refreshing that content so that the updated content is served in the future
- Why do you use Hooks instead of fetch or data stream counting?
- Because Hooks can reach the UI life cycle
- Asynchronous logic is described in synchronous writing because rendering is performed twice
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>; }
- Hooks is more powerful than that. The short lines of code above also have the following features:
- Can refresh automatically
- Local caching is preferred when components are destroyed and rendered
- Browser fallback automatically remembers the scrollbar position on the list page
- When tabs are switched, the focus tab recalculates
How to use “useSwr”?
Not using “useSwr”
import React from "react"; import "./App.css"; const todosEndpoint = "http://localhost:3001/todos"; const TodoApp = () => { const [todos, setTodos] = React.useState([]); React.useEffect(() => { const getData = async () => { const response = await fetch(todosEndpoint); const data = await response.json(); setTodos(data); }; getData(); }, []); return ( <div className="App"> {todos.map((todo) => ( <div key={todo.id}>{todo.title}</div> ))} </div> ); }; export default TodoApp;
Use “useSwr”
import React from "react"; import useSWR from "swr"; const todosEndpoint = "http://localhost:3001/todos"; const getData = async () => { const response = await fetch(todosEndpoint); return await response.json(); }; const TodoApp = () => { const { data: todos } = useSWR(todosEndpoint, getData); return ( <div> {todos && todos.map(todo => ( <div key={todo.id}>{todo.title}</div> )} </div> ); }; export default TodoApp;
useSWR
doesn’t care how you fetch data. You can use any asynchronous data fetching strategy that you like. You can use fetch
, axios
, even GraphQL. As long as your function asynchronously returns your data, useSWR
is happy.
A call to useSWR
returns the following parameters
data
: data for the given key resolved by the fetcher (undefined if not yet loaded)error
: the error thrown by the fetcherisValidating
: boolean representing whether there is a request or revalidation in progressmutate(data?, shouldRevalidate)
: a function to mutate the cached data
How to apply changes to data and update view?
By Using mutate, we can apply local changes to our data, and update our view, without waiting for the remote source of data to update
useSWR will even revalidate and replace it with the latest data in the background
Example: Fetch a single todo, render a checkbox to toggle the completed
status of the todo. When the checkbox is clicked, we make a PUT
request to update our todo, and we call mutate
to update useSWR’s
cache.
import React from "react"; import useSWR from "swr"; import "./App.css"; const todosEndpoint = "http://localhost:3001/todos"; const getTodo = async (id) => { const response = await fetch(`${todosEndpoint}/${id}`); return await response.json(); }; const updateTodo = async (id, todo) => { const response = await fetch(`${todosEndpoint}/${id}`, { method: "PUT", headers: { "Content-type": "application/json; charset=UTF-8", }, body: JSON.stringify(todo), }); return await response.json(); }; const TodoApp = () => { const todoId = 1; const key = `${todosEndpoint}/${todoId}`; const { data: todo, mutate } = useSWR(key, () => getTodo(todoId) ); const toggleCompleted = async () => { const newTodo = { ...todo, completed: !todo.completed, }; await updateTodo(todoId, newTodo); mutate(newTodo); }; if (!todo) { return <div>Loading...</div>; } return ( <div> <p>{todo.title}</p> <div> <input type="checkbox" id="completed" name="completed" value="completed" checked={todo.completed} onChange={toggleCompleted} /> <label htmlFor="completed">Completed</label> </div> </div> ); }; export default TodoApp;
How to do data polling?
const { data: todos } = useSWR( todosEndpoint, getData, { refreshInterval: 5000 } // 5s interval refresh );
How to request API conditionally?
This can be used to force SWR to request the API again too (do not use cache)
import useSWR from 'swr'; const Form: React.FC = ({ isOpen }) => { const { data } = useSWR( isOpen ? 'public_api/v2/signed_uri' : null, () => fetcher()); ... return <></> }
Leave a Reply