React – How to use “useSwr”?

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 fetchaxios, 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

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 <></>
}

How to request API depending on data of previous API request?

Is second request refreshed when first API is requested by polling?

Be the first to comment

Leave a Reply

Your email address will not be published.


*