Search code examples
reactjsnext.jsswr

SWR: immediately update UI when mutating data


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

Solution

  • 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",
          },
        })         
    }