So we thought to ourselves, there must be a better way.
And there is. It’s even made by Vercel — the creators of NextJS.
Enter SWR
SWR gets its name from stale-while-revalidate, a caching strategy that is gaining popularity in the frontend realm. It allows us to load cached content right away, while at the same time refreshing that content so that the updated content is served in the future. For us, it’s the perfect trade-off between performance and user experience.
useSWR — how do I use it?
useSWR is a React Hook library made by Vercel. It fetches data from an API or other external source, then saves that data in a cache, and renders the data.
Let’s start by looking at an example of a React component that fetches a list of TODOs from JSON Server, and renders them.
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;
Next, let’s look at the same component, this time refactored to useuseSWR.
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;
As you can see, it looks quite similar to the previous implementation.
In this example, we use the useSWR(key, fetcher, options) to fetch our todo list. The key is used by useSWR for caching. In our case, we used the todos endpoint. For our fetcher, we passed in an asynchronous function that fetches our list of todos.
It’s important to note that useSWR is unopinionated about 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 fetcher
isValidating: boolean representing whether there is a request or revalidation in progress
mutate(data?, shouldRevalidate): a function to mutate the cached data
A more complex example
Let’s look at a more complex example that utilizes more of the parameters that useSWR returns. In this example, we fetch a single todo, and we 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;
Using mutate is a great way to improve the perceived performance of your web application. 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.
Why do I need a library for this?
You might ask yourself this question. You might already have a state solution in your application, and you might not see the value in using a third-party library for something as seemingly simple as data fetching and caching.
In response to this, I will tell you that caching data is hard. And it only gets harder as your application gets larger. You will run into edge cases, and you will end up with complex stores and providers that are hard to understand, and harder to maintain. Bugs will spring up, seemingly out of nowhere.
There are only two hard things in Computer Science: cache invalidation and naming things — Phil Karlton
Instead of rolling your own data fetching solution, why not rely on a battle-tested one, built by one of the most respected companies in the React ecosystem?
What useSWR did for us
Migrating our application to use useSWR for data fetching gave us numerous benefits.
1. It allowed us to delete code
Pull requests that delete code, rather than add code, are my favourite to open. The less code in our application, the less chance for bugs, the better. As I get older, I have started to appreciate simplicity more and more. It allowed us to delete entire unstated stores and made our application simpler and easier to understand.
2. It made it easier to onboard new developers to the project
useSWR, like most open source projects, has great documentation. Rolling our own solution meant we needed to write documentation and teach developers new to the project how to deal with data fetching. Now that we use SWR, we don’t have to do this.
3. It makes the hard things easy
Our application, like most applications out there, has lots of API calls. Some of those API calls depend on the results of other API calls. With useSWR, it’s easy to write hooks for dependant fetching.
4. Perceived performance has improved
The app looks and feels snappier. Users appreciate this, and the feedback has been good.
5. It refreshes stale data
useSWR refreshes stale data on focus. This means our users always have the most up to date version of their data, without the loading times.
Conclusion
useSWR has had a huge impact on our application. It simplified our code and bettered our user experience. I can’t think of another library that was this easy to implement and offered benefits as useSWR. For more information about useSWR, be sure to check out both their website and their Github repository.
Alternative libraries
useSWR is not the only great fetch fetching library out there. While useSWR has worked well for my use case, maybe a different data fetching library will work better for yours.
React-Query
react-query is a library made up of hooks for fetching, caching and updating asynchronous data in React. Written by Tanner Linsey, it’s a very popular solution that feels a similar niche to useSWR.
Apollo
Apollo Client is a GraphQL client that will take care of requesting and caching your data, and updating your UI. If you use GraphQL, Apollo is a great solution.
WRITTEN BY
No comments:
Post a Comment