Skip to content

optimisticData function does not use fallback as currentData #2114

@djfarly

Description

@djfarly

Bug report

When using the optimisticData function (see #1850) does not use data from SSR fallback as currentData.
As long as SWR has not revalidated the fallback data you can't really use it as basis for new optimisticData.

Description / Observed Behavior

consider this example

const myListResponse = useSWR(`api/myList`);

async function addToList(item) {
  await myListResponse.mutate(
    async () => { /* some fetch PATCH adding the item to the list */ },
    {
        populateCache: true,
        rollbackOnError: true,
        revalidate: false,
        optimisticData(currentData?) {
          return [...(currentData ?? []), item];
        },
      }
  )
}

Even if fallback: { "api/myList": ["foo", "bar"] } is set in <SWRConfig> the currentData argument is undefined when addToList() is called before the fallback has been revalidated.

This leads to undesired results:

  • data is ['foo', 'bar'] initially (from fallback)
  • addToList('baz') is called
  • data is now ['baz'] (from optimisticData function) because currentData was undefined but should have been ['foo', 'bar']
  • fetched data arrives
  • data is now ['foo', 'bar', 'baz']

In my case addToList it is called from an effect but it could also be caused by a fast clicking user or slow network connection.

Expected Behavior

currentData should use fallback data if it can.

And the process should be like this:

  • data is ['foo', 'bar'] initially (from fallback)
  • addToList('baz') is called
  • data is now ['foo', 'bar', 'baz'] (from optimisticData function) because currentData was ['foo', 'bar']
  • fetched data arrives
  • data still is ['foo', 'bar', 'baz']

Repro Steps / Code Example

See snippet above - I'm not sure how to best create a repro without building a sample api. 🙈

Additional Context

SWR version.
2.0.0-beta.6 in next 12.2

I have found two possible workarounds.

  1. don't mutate if still loading:
async function addToList(item) {
  if (myListResponse.isLoading) return;

  await myListResponse.mutate(
  
  1. fallback to fallback data manually:
        optimisticData(currentData?) {
          currentData = currentData ?? myListResponse.data;

          return [...(currentData ?? []), item];
        },

Both seem suboptimal.

Thanks for investigating this. ❤️

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions