Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cache not updating after mutation when using fallback data in useQuery #502

Open
nekusu opened this issue Sep 13, 2024 · 6 comments
Open
Labels
bug Something isn't working

Comments

@nekusu
Copy link

nekusu commented Sep 13, 2024

Describe the bug
When using mutation hooks with fallback data set in the useQuery hook, the cache does not update after triggering a mutation. Specifically, after passing fallback data to useQuery via fetchQueryFallbackData, useUpdateMutation doesn't seem to update the cache as expected.

To Reproduce
Steps to reproduce the behavior:

  1. Implement useQuery with fallbackData as shown in the example below:
    // Server component
    const [, fallbackData] = await fetchQueryFallbackData(
       supabase.from('users').select('id,avatar_url,name').eq('id', id).single(),
     );
    
    // Client component
    const { data: user } = useQuery(
      supabase.from('users').select('id,avatar_url,name').eq('id', id).single(),
      { fallbackData },
    );
  2. Trigger a mutation using useUpdateMutation to update a user record:
    const { trigger: updateUser } = useUpdateMutation(supabase.from('users'), ['id']);
  3. Observe that the cache doesn't update after the mutation is triggered, even though it should.

Expected behavior
The cache should be updated after a mutation when using useQuery with fallbackData.

Additional context

  • Next.js v14 using the app router.
  • Setting revalidateIfStale: true, either directly in the hook or globally via SWRConfig, resolves the issue and updates the cache as expected.
@nekusu nekusu added the bug Something isn't working label Sep 13, 2024
@nekusu
Copy link
Author

nekusu commented Sep 13, 2024

Additionally, the Supabase clients I'm using for both the server and the client are set up following this guide: https://supabase.com/docs/guides/auth/server-side/nextjs?queryGroups=router&router=app

@psteinroe
Copy link
Owner

did you try to pin-point the cause? cache updates are working in normal circumstances, and I wonder what causes it to break here. if revalidateIfStale: true causes a revalidation, then the cache item is hit correctly and the update is not being applied. can you show the full code, including how you call the updateUser mutation? does removing the fallbackData change anything?

@nekusu
Copy link
Author

nekusu commented Sep 13, 2024

I’ve made some progress on the issue. Here’s what I found:

I noticed that when passing only the fallbackData directly to the useQuery hook, the cache was not populated correctly. The SWR cache would only contain the key without any data. This was confirmed by inspecting the cache with:

const { cache } = useSWRConfig();

When fallbackData is set, the value for the cache key doesn’t include the data, which causes mutations not to work properly. However, when I remove fallbackData, the cache correctly contains the data and updates as expected.

What worked:

Instead of passing just fallbackData to the useQuery hook, I used both the key and the data returned from fetchQueryFallbackData and passed them to SWRConfig (setting fallback instead of fallbackData in useQuery also works). This ensured that the cache was populated correctly and that mutations worked.

Hypothesis:

It seems that when passing only fallbackData, SWR treats it as a temporary value for the initial render, but it doesn’t associate the data with the cache key or persist it. As a result, the cache isn’t set properly and subsequent mutations fail to apply. By using the key generated by fetchQueryFallbackData, the cache is correctly associated with the data, allowing it to be updated by mutations. This issue arises specifically when revalidateIfStale is set to false, otherwise the cache would be populated on mount as normally.

I believe this should be corrected in the documentation. Currently, it mentions that you can either pass fallbackData directly to useSWR or define it globally in SWRConfig, but it seems that they don't behave the same way. The correct procedure should be set fallback on either the useQuery hook or globally in SWRConfig.

Let me know if you agree with the analysis, I can do a PR updating the docs!

@nekusu
Copy link
Author

nekusu commented Sep 14, 2024

After further testing I realized that I was wrong about the solution in my previous comment. I was incorrectly setting the fallback in SWRConfig.

Initially I was passing it as [string, any] as returned by the fetchQueryFallbackData function instead of { [key: string]: any }, meaning that the fallback was being ignored, causing the cache to not have that initial key and value at all and triggering a revalidation on mount. Once I fixed that and set the fallback correctly, the behavior matched what I observed when using fallbackData directly in the hook.

This means that currently, neither approach correctly handles the fallback, setting fallback correctly in SWRConfig produces the same behavior as setting fallbackData directly in the hook, the key in the cache is generated but its value isn't populated with the data regardless of where fallback is set and mutations fail to apply.

@psteinroe
Copy link
Owner

thanks for the detailed investigation! Did you confirm the same behaviour with using plain swr? I am struggling to find the cause in cache helpers for this.

@nekusu
Copy link
Author

nekusu commented Oct 13, 2024

I ended up using the react-query package. If I remember correctly, the root cause of the issue was that setting fallbackData in SWR does not prepopulate the cache. As a result, subsequent mutations fail because there is no existing cache data to mutate.

This behavior contrasts with react-query, where you have Initial Query Data and Placeholder Query Data. initialData is persisted to the cache, while placeholderData is not. In SWR, fallbackData behaves similarly to placeholderData, meaning it only serves as a temporary placeholder when data is undefined.

Found a comment that proves this: vercel/swr#2114 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants