swr
swr copied to clipboard
`optimisticData` function does not use fallback as `currentData`
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']
(fromoptimisticData
function) becausecurrentData
wasundefined
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']
(fromoptimisticData
function) becausecurrentData
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.
- don't mutate if still loading:
async function addToList(item) {
if (myListResponse.isLoading) return;
await myListResponse.mutate(
…
- fallback to fallback data manually:
optimisticData(currentData?) {
currentData = currentData ?? myListResponse.data;
return [...(currentData ?? []), item];
},
Both seem suboptimal.
Thanks for investigating this. ❤️
Thanks for reporting this! Sounds like a fair case to cover 👍
After dicussing with @shuding , it's more like by designed currently. since the fallbackData
is per hook, you can have different fallback value for the same resource. But mutate()
works per resource, so you might not be able to know the fallback value for each hook.
@huozhi I'm facing the same problem as @djfarly at the moment. The documentation indeed states that fallbackData
is per hook, but to my understanding fallback
is not. Still the data stored as fallback
is not being use as currentData
. Is this by design as well?
Currently we're not using it to commit the data into the cache, the fallback
and the fallbackData
are all for fallback purpose when data is undefined
. Even though fallback
is set through SWR configuration, it doesn't fill data into cache.
So in your case I think you'd more like to use mutate
to fill the cache first, or just share the fallback
value between the mutation fetcher and swr configuration