reactjsnext.jsfetch-apiswr

useSWR doesn't work with async fetcher function


I am using SWR to fetch data to populate a table with. I am using the following code:

const { data: items, error } = useSWR(fetchAllItems.name, fetchAllItems)

The fetcher function looks like this

async function fetchAllItems() {
  const response = await fetch('http://localhost:3000/items')
  const data = await response.json()
  return data
}

The table also has an options to add a new item. After creating the new item I call mutate to let SWR fetch the new data. While running this in development mode it all works great, however when using next start instead of next dev it won't fetch the data. In the networks tab it shows 0 requests, so it looks like the fetcher function is never called.

The server where the data is fetched from is running properly. I have tested this using postman and it returns all the items. It is also accessible from the next application, because creating a new item works fine.

EDIT: After some more debugging I have narrowed it down to the fact that SWR is never validating when using next start instead of next dev.

EDIT 2: A codesandbox with a minimal reproducible example can be found here https://codesandbox.io/s/elated-cherry-3ugqyi?file=/package.json . De codesandbox runs the program using npm run dev (which calls next dev) so everything works fine. However when running it with npm run build && npm run start useSWR no longer calls the fetcher function.

EDIT 3: I have resolved the issue, but don't understand how. I changed the fetcher function to the following:

function fetchAllItems() {
  return fetch('http://localhost:3000/items').then((res) => res.json())
}

The fetcher function is now no longer async, however it still returns a Promise (because fetch returns a Promise). If anyone could explain why this code behaves differently from the previous that would be great.


Solution

  • Your fetcher function is fine as is. The issue occurs because in the production build fetchAllItems.name resolves to an empty string '', meaning useSWR will not call the fetcher function as the key param is a falsy value.

    You have two options to solve the problem.

    #1 Use hardcoded string as useSWR key

    Simply use a string as the key parameter in the useSWR. I'd recommend this approach, as using fetchAllItems.name may not always return the expected value in production.

    const { data: items, error } = useSWR('fetchAllItems', fetchAllItems)
    

    #2 Use named function for fetchAllItems

    Use the named function format for the fetchAllItems function, instead of an arrow function. This was probably what fixed the issue for you when you defined the new function without async/await.

    export async function fetchAllItems() {
        const response = await fetch('http://localhost:3000/items')
        const data = await response.json()
        return data
    }