fervor [>]CODING & CURIOSITY
FERVOR LEARNING SYSTEMTUTORIALS
← React

React / 7 MIN READ

Fetching with SWR

Data fetching using SWR in React

From the original Fervor library. Examples may use older package versions.

Let’s dive into SWR in React. SWR stands for “stale-while-revalidate,” a fetching library created by the team at Vercel. It’s super handy for handling data fetching in React, keeping it fresh, and ensuring your UI is always up-to-date. Think of it like having a smart assistant who keeps your data updated in the background while you focus on building cool features.

Step 1: Install SWR

First, you need to install SWR in your existing React project. Open your terminal and run:

npm install swr

Or if you prefer yarn:

yarn add swr

Step 2: Basic Setup

To use SWR, you need to import the useSWR hook into your component. Here’s a basic example of how to use it to fetch data:

import React from 'react';
import useSWR from 'swr';

const fetcher = (url) => fetch(url).then((res) => res.json());

const App = () => {
  const { data, error, isLoading } = useSWR('https://api.example.com/data', fetcher);

  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error loading data</div>;

  return (
    <div>
      <h1>Fetched Data</h1>
      <pre>{JSON.stringify(data, null, 2)}</pre>
    </div>
  );
};

export default App;

Step 3: Breaking Down the Code

  1. Importing useSWR: The useSWR hook is imported from the swr package.
  2. Fetcher Function: This function is responsible for fetching data. It takes a URL and returns the JSON response. SWR uses this function to fetch data from the given URL.
  3. useSWR Hook: The hook is called with the URL of the data and the fetcher function. It returns an object containing data, error, and isLoading.

Step 4: Handling Loading and Error States

SWR provides isLoading and error properties to handle the loading and error states:

if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error loading data</div>;

This ensures that your component can gracefully handle the different states of the data fetching process.

Step 5: Optimistic UI Updates

One of the cool features of SWR is optimistic UI updates. This means you can update the UI before the data fetching is complete. Here’s an example of how you might implement it:

import React, { useState } from 'react';
import useSWR, { mutate } from 'swr';

const fetcher = (url) => fetch(url).then((res) => res.json());

const App = () => {
  const { data, error, isLoading } = useSWR('https://api.example.com/data', fetcher);
  const [newData, setNewData] = useState('');

  const handleAddData = async () => {
    const updatedData = [...data, newData];
    mutate('https://api.example.com/data', updatedData, false);
    
    await fetch('https://api.example.com/data', {
      method: 'POST',
      body: JSON.stringify(newData),
    });

    mutate('https://api.example.com/data');
  };

  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error loading data</div>;

  return (
    <div>
      <h1>Fetched Data</h1>
      <pre>{JSON.stringify(data, null, 2)}</pre>
      <input type="text" value={newData} onChange={(e) => setNewData(e.target.value)} />
      <button onClick={handleAddData}>Add Data</button>
    </div>
  );
};

export default App;

Step 6: SWR Configuration

You can also configure SWR globally to set default behaviors:

import React from 'react';
import useSWR, { SWRConfig } from 'swr';

const fetcher = (url) => fetch(url).then((res) => res.json());

const App = () => {
  return (
    <SWRConfig value={{ fetcher }}>
      <ComponentThatUsesSWR />
    </SWRConfig>
  );
};

const ComponentThatUsesSWR = () => {
  const { data, error, isLoading } = useSWR('https://api.example.com/data');

  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error loading data</div>;

  return (
    <div>
      <h1>Fetched Data</h1>
      <pre>{JSON.stringify(data, null, 2)}</pre>
    </div>
  );
};

export default App;

In this setup, all components inside the SWRConfig will use the same fetcher function.

Step 7: Advanced Features

SWR also supports a range of advanced features like:

  • Pagination: Efficiently handling paginated data.
  • Dependent Fetching: Fetching data that depends on another piece of data.
  • Prefetching: Preloading data for a smoother user experience.

For more detailed use cases, you can explore the official SWR documentation.

Conclusion

SWR is a powerful tool for data fetching in React, offering features like caching, revalidation, and optimistic UI updates out of the box. With this tutorial, you should have a good starting point to implement SWR in your React projects and take your data fetching to the next level.

Happy coding! 🚀

Bonus coolness

SWR is super versatile and can handle a variety of data fetching scenarios beyond just simple data fetching. Here are some cool things you can do with SWR:

1. Pagination

Handling pagination is straightforward with SWR. You can create a function that fetches paginated data and then use SWR to manage the pagination state.

import React, { useState } from 'react';
import useSWR from 'swr';

const fetcher = (url) => fetch(url).then((res) => res.json());

const PaginatedComponent = () => {
  const [pageIndex, setPageIndex] = useState(0);
  const { data, error, isLoading } = useSWR(`https://api.example.com/data?page=${pageIndex}`, fetcher);

  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error loading data</div>;

  return (
    <div>
      <h1>Paginated Data</h1>
      <ul>
        {data.items.map((item) => (
          <li key={item.id}>{item.name}</li>
        ))}
      </ul>
      <button onClick={() => setPageIndex((prev) => Math.max(prev - 1, 0))} disabled={pageIndex === 0}>
        Previous
      </button>
      <button onClick={() => setPageIndex((prev) => prev + 1)} disabled={!data.hasNextPage}>
        Next
      </button>
    </div>
  );
};

export default PaginatedComponent;

2. Dependent Fetching

Sometimes you need to fetch data that depends on another piece of data. SWR makes it easy with conditional fetching.

import React, { useState } from 'react';
import useSWR from 'swr';

const fetcher = (url) => fetch(url).then((res) => res.json());

const DependentFetchingComponent = () => {
  const [userId, setUserId] = useState(null);
  const { data: user, error: userError, isLoading: isLoadingUser } = useSWR(
    userId ? `https://api.example.com/user/${userId}` : null,
    fetcher
  );

  const { data: posts, error: postsError, isLoading: isLoadingPosts } = useSWR(
    user ? `https://api.example.com/posts?userId=${user.id}` : null,
    fetcher
  );

  if (isLoadingUser || isLoadingPosts) return <div>Loading...</div>;
  if (userError) return <div>Error loading user data</div>;
  if (postsError) return <div>Error loading posts</div>;

  return (
    <div>
      <input
        type="number"
        value={userId || ''}
        onChange={(e) => setUserId(e.target.value)}
        placeholder="Enter user ID"
      />
      {user && (
        <div>
          <h1>{user.name}'s Posts</h1>
          <ul>
            {posts.map((post) => (
              <li key={post.id}>{post.title}</li>
            ))}
          </ul>
        </div>
      )}
    </div>
  );
};

export default DependentFetchingComponent;

3. Mutations and Optimistic Updates

You can use SWR to perform mutations and optimistically update the UI. This gives users instant feedback before the server confirms the changes.

import React, { useState } from 'react';
import useSWR, { mutate } from 'swr';

const fetcher = (url) => fetch(url).then((res) => res.json());

const OptimisticUpdateComponent = () => {
  const { data, error, isLoading } = useSWR('https://api.example.com/data', fetcher);
  const [newItem, setNewItem] = useState('');

  const handleAddItem = async () => {
    const updatedData = [...data, { id: Date.now(), name: newItem }];
    mutate('https://api.example.com/data', updatedData, false);

    await fetch('https://api.example.com/data', {
      method: 'POST',
      body: JSON.stringify({ name: newItem }),
    });

    mutate('https://api.example.com/data');
  };

  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error loading data</div>;

  return (
    <div>
      <h1>Data with Optimistic Update</h1>
      <ul>
        {data.map((item) => (
          <li key={item.id}>{item.name}</li>
        ))}
      </ul>
      <input
        type="text"
        value={newItem}
        onChange={(e) => setNewItem(e.target.value)}
        placeholder="New item"
      />
      <button onClick={handleAddItem}>Add Item</button>
    </div>
  );
};

export default OptimisticUpdateComponent;

4. Prefetching

Prefetching data for a smoother user experience is easy with SWR. You can prefetch data before the user navigates to a new page.

import React from 'react';
import useSWR, { mutate } from 'swr';
import { Link } from 'react-router-dom';

const fetcher = (url) => fetch(url).then((res) => res.json());

const PrefetchingComponent = () => {
  const { data, error, isLoading } = useSWR('https://api.example.com/data', fetcher);

  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error loading data</div>;

  const handlePrefetch = () => {
    mutate('https://api.example.com/other-data', fetch('https://api.example.com/other-data').then((res) => res.json()));
  };

  return (
    <div>
      <h1>Data</h1>
      <ul>
        {data.map((item) => (
          <li key={item.id}>{item.name}</li>
        ))}
      </ul>
      <Link to="/other-page" onMouseEnter={handlePrefetch}>
        Go to Other Page
      </Link>
    </div>
  );
};

export default PrefetchingComponent;

5. Real-Time Data with WebSockets

Combine SWR with WebSockets to handle real-time data updates efficiently.

import React, { useEffect } from 'react';
import useSWR, { mutate } from 'swr';

const fetcher = (url) => fetch(url).then((res) => res.json());

const RealTimeComponent = () => {
  const { data, error, isLoading } = useSWR('https://api.example.com/data', fetcher);

  useEffect(() => {
    const socket = new WebSocket('wss://api.example.com/realtime');

    socket.onmessage = (event) => {
      const newData = JSON.parse(event.data);
      mutate('https://api.example.com/data', newData, false);
    };

    return () => {
      socket.close();
    };
  }, []);

  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error loading data</div>;

  return (
    <div>
      <h1>Real-Time Data</h1>
      <ul>
        {data.map((item) => (
          <li key={item.id}>{item.name}</li>
        ))}
      </ul>
    </div>
  );
};

export default RealTimeComponent;

These examples showcase SWR’s flexibility and power, making it an excellent choice for various data-fetching scenarios in React applications. Happy coding! 🚀

Keep your curiosity going.Explore more React →
287 TUTORIALS · 22 TOPICSREADY