frost2709
frost2709

Reputation: 385

The optimal way to mutate data using SWR and NextJS

I'm currently using SWR to fetch my data for client components in a NextJS project. I was looking for the most optimal way to write the function as I feel like my solution is a little hacky and I'm struggling with implementing the docs solution.

What I currently have is a route that returns user data.

A custom useUser hook that looks like this:

import axios from "axios";
import useSWR from "swr";

const useUser = () => {
  // url
  const url = `http://localhost:3000/api/user`;

  // fetcher
  const fetcher = (url) => axios.get(url).then((res) => res.data);

  // swr
  const { data, error, isLoading, mutate } = useSWR(url, fetcher);

  return {
    user: data,
    loading: isLoading,
    error,
    mutate,
  };
};

export default useUser;

Then I implement in a page like so:

import useUser from "@/utils/hooks/useUser";
import {useState} from "react";

const Dashboard = () => {
 // state
 const [modalOpen, setModalOpen] = useState(false); 
 const [id, setId] = useState(null);

 // swr destructure
 const { user, loading, error, mutate } = useUser();


 return (
  <div>
   <h1>Dashboard</h1>
   <button
    onClick={async () => {
     
     // mutate local data
     mutate(
      (data) => {
       return {
        ...data,
        quizzes: data.quizzes.filter((quiz) => quiz.id !== id),
       };
      },
      {
       revalidate: false,
      }
     );

     // ui responsive, close modal on delete
     setModalOpen(false);

     try {
      // delete quiz on the backend
      await fetch(`http://localhost:3000/api/quiz?quizId=${id}`, {
       method: "DELETE",
      });
      
      // revalidate the data coming from the useUser hook
      await mutate();
     } catch (e) {
      console.log(e);
     } finally {
      setId(null);
     }
    }}
   >Delete</button>
  </div>
 )

}

Anyone have a better suggestion?

As I've mentioned above.

Upvotes: 0

Views: 1485

Answers (1)

Dipsundar Jana
Dipsundar Jana

Reputation: 34

In your Dashboard component, when you delete a quiz, you're locally updating the data via mutate, then making a backend request to delete the quiz, and finally revalidating the data with mutate(). However, it seems like you're not passing the updated data to mutate after the deletion.

One way to address this issue is by providing the new data to mutate after deleting the quiz. Here's an updated version:

const Dashboard = () => {
  const [modalOpen, setModalOpen] = useState(false);
  const [id, setId] = useState(null);

  const { user, loading, error, mutate } = useUser();

  const handleDelete = async () => {
    try {
      // Delete quiz on the backend
      await fetch(`http://localhost:3000/api/quiz?quizId=${id}`, {
        method: "DELETE",
      });

      // Filter quizzes locally
      const updatedQuizzes = user.quizzes.filter((quiz) => quiz.id !== id);

      // Update the local data by providing the new data to mutate
      mutate({ ...user, quizzes: updatedQuizzes }, false);

      setModalOpen(false); // Close modal
      setId(null);
    } catch (e) {
      console.log(e);
    }
  };

  return (
    <div>
      <h1>Dashboard</h1>
      <button onClick={handleDelete}>Delete</button>
    </div>
  );
};

This code simplifies the deletion process by filtering the quizzes locally, then updating the data with mutate. This way, you're passing the updated data to mutate after the deletion, avoiding the need to revalidate the entire data set from the server.

Upvotes: 0

Related Questions