SWR: immediately update UI when mutating data

Viewed 2668

I want the data in the UI to update immediately rather than waiting for SWR to sync with the database.

I've tried following the docs, but the UI still won't update automatically (I switch away and back to my browser to update it). Here's the code:

import useSWR, { useSWRConfig } from "swr";

export default function Tasks() {
  const { mutate } = useSWRConfig()
  const { data } = useSWR("/api/tasks", (...args) =>
    fetch(...args).then((res) => res.json())
  )

  const deleteTask = async (taskId) => {
    const newData = data.filter((task) => task.id !== taskId)

    mutate(`/api/tasks/${taskId}`, newData, false)

    await fetch(`/api/tasks/${taskId}`, {
      method: "DELETE",
      headers: {
        "Content-Type": "application/json",
      },
    })

    mutate(`/api/tasks/${taskId}`)
  }

  if (!data) return <p>loading</p>

  return (
    <div>
      {data.map((task) => (
        <div key={task.id}>
          <div>{task.content}</div>
          <button onClick={() => deleteTask(task.id)}>Delete</button>
        </div>
      ))}
    </div>
  )
}
2 Answers

The problem is that your are calling the wrong mutate function. You are expecting data to get refreshed while you are calling mutate on mutate(`/api/tasks/${taskId}`)

First, make the following modification

const { data, mutate: mutateList } = useSWR("/api/tasks", (...args) =>
    fetch(...args).then((res) => res.json())
 )

Now, in your delete task do the following

const deleteTask = async (taskId) => {
    const newData = data.filter((task) => task.id !== taskId)

   // The useSWR("/api/tasks"...) returns a mutate method bound to swr's key, use it! 
   // Passing false will prevent revalidation so a fetch request 
   // won't be made to your api on the other hand if you pass true 
   // then a fetch request will be made to your api. In both cases
   // you should see your cache refreshed immediately and that
   // should update your UI. Since you haven't as yet updated your
   // backend i.e. you are calling mutate _before_ calling your 
   // api, you'll want to pass false as 2nd argument. 
   // Had you called mutate after calling your api then you could 
   // have passed true as second argument to revalidate your cached
   // data. Passing true as second argument in the function below
   // doesn't make sense since you have yet to update your 
   // server/backend.
    await mutateList(newData, false);

    await fetch(`/api/tasks/${taskId}`, {
      method: "DELETE",
      headers: {
        "Content-Type": "application/json",
      },
    })         
}

If you read more of the doc page you can find the bounded mutate, it's function returned by useSWR hook. and its the easiest way to revalidate the data. the doc

  const { data, mutate } = useSWR('/api/user', fetcher)
  ...
  mutate() //run mutation
Related