useSWR is my new favorite React library
The last few months I have been working on an application on NextJS. Every week it becomes more and more. The application uses axios for API calls and unstated-next to manage the state. There are a lot of API calls, but we don’t want users to see a bunch of loading screens. Therefore we store call results axios at unstated storage facilities
.
However, we ran into a problem. Vaults themselves are becoming more and more complex. Pages sometimes require multiple API calls, and they in turn rely on the results of other API calls. Days turned into weeks and weeks into months, meanwhile our unstated vaults became more and more cumbersome. We ran into strange errors as our homemade caching logic struggled to deal with unexpected extreme cases.
Then we thought that the path should be simpler.
And here he is. And it is even designed Vercelcreator NextJS.
Get to know SWR
The name SWR comes from stale-while-revalidate, a caching method that is currently gaining popularity in frontend development. It allows you to download cached content immediately, and immediately updates it to handle new content in the future. In our case, we got the perfect compromise between performance and user experience.
How to use useSWR
useSWR Is a React hook library developed by Vercel. It allows you to retrieve data from an API or other external source, save it in the cache, and then render it.
Let’s start by looking at an example of a React component that gets a TODO list with JSON server, and renders it.
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 (
{todos.map((todo) => (
{todo.title}
))}
);
};
export default TodoApp;
Now let’s look at the same component, but rewritten with 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 (
{todos && todos.map(todo => (
{todo.title}
)}
As you can see, it is very similar to the previous implementation.
In this example, we use useSWR (key, fetcher, options)to get our todo list. Key useSWR is needed for caching. In our case, we used todosEndpoint. As fetcher we passed an asynchronous function that retrieves a TODO list.
It should be noted that useSWR has no idea how you retrieve data. You can use any asynchronous data mining strategy that you like. you can use fetch, axios or even GraphQL. While your function is returning data asynchronously, useSWR will be happy.
Call useSWR returns the following parameters:
- data: key data obtained with fetcher (not defined if not already loaded);
- error: error caused by fetcher;
- isValidating: a boolean that says whether a query or revalidation is being performed;
- mutate (data ?, shouldRevalidate): function to change cached data.
More complex example
Let’s look at a more complex example that uses more parameters returned useSWR. In this example, we will retrieve one element of the TODO list and render the checkbox for the status complete our task from the list. When the checkbox is clicked, we send a PUT request to update TODO, and then call mutate to update the cache useSWR.
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 Loading...
{todo.title}