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

Define when suspense queries should be refetched and when they should throw #1059

Closed
trevorwhealy opened this issue Sep 18, 2020 · 18 comments
Closed
Labels
enhancement New feature or request help wanted Extra attention is needed needs-info suspense

Comments

@trevorwhealy
Copy link

Describe the bug
When a user does client-side navigation to return to a previously visited page (react-router-dom), the useQuery hook is not invoked again, even if the query has been marked as invalid. This is an issue because the server value could change in a different browser session or device and the useQuery data value would no longer be in sync.

To Reproduce
Created a reproducible environment for testing the bug here:
https://codesandbox.io/s/ping-pong-react-query-client-guqhn
Important files: src/index.js and src/components/Demo.js

Steps to reproduce the behavior:

  1. Visit Page 1, note the number of pings, and click the increment button.
  2. In the mutation's onSuccess handler, we invalidate the queryCache and navigate to Page 2.
  3. Open a new tab and simulate the ping mutation by simply hitting this URL a few times:
    -- https://1675g.sse.codesandbox.io/ping-increase
  4. Navigate back to Page 1 via Link or browser back button and note that the value is only incremented by 1.
  5. If you refresh Page 1, you'll notice that the value now reflects the actual server value.

Expected behavior
On return to Page 1 from Page 2, I would expect the useQuery hook to fetch the latest server value.

@tannerlinsley
Copy link
Collaborator

Can't remember what I changed, but is this example working for you as expected?

https://codesandbox.io/s/ping-pong-react-query-client-forked-pbc4e?file=/src/components/Demo.js

@trevorwhealy
Copy link
Author

@tannerlinsley thanks for the really quick reply! I had the same problem - maybe I'm not doing a good job of illustrating.

When I navigate back to Page 1 from Page 2, you'll notice that the Query stays in an Inactive state:

ezgif com-video-to-gif (1)

I was expecting it to re-fetch this query when the component re-mounts.

@trevorwhealy
Copy link
Author

I think that would require the query to suspend if it is in the isFetching state in addition to the isLoading state

@boschni
Copy link
Collaborator

boschni commented Sep 19, 2020

Think there are two issues here:

  1. Devtools is not notified when observers unsubscribe (if you reselect the query then it shows the correct state).
  2. There is some specific code to prevent refetching when suspense is enabled. This is probably to prevent double fetches because the default stale time is 0 and the hook does not know if it is rendering right after a suspense or after some page switch. Unfortunately this means suspended queries are never refetched. Is my assumption correct @tannerlinsley ?

@tannerlinsley
Copy link
Collaborator

tannerlinsley commented Sep 19, 2020 via email

@boschni
Copy link
Collaborator

boschni commented Sep 25, 2020

I think we need to define the behavior when using suspense. Currently it will only trigger suspense when it is loading for the first time. Any additional mounts will not trigger a refetch, which does not sound desirable. Maybe we should set a default stale time when suspense is enabled? Then the query will at least be refetched again if it re-mounts after some time. Then the other question is, when do we want to trigger suspense? Only on first load, or also on refetches? Or does it need to be configureable?

@boschni boschni changed the title Client Side Navigation not Invoking useQuery after Initial Mount Define when suspense queries should be refetched and when they should throw Sep 26, 2020
@flybayer
Copy link
Contributor

I'm pretty sure that once useEffect() fires, you know for sure that it has fully mounted. From my understanding, useEffect will not be called multiple times during suspense rendering/re-rendering. It only triggers once the render has fully finished.

@boschni
Copy link
Collaborator

boschni commented Oct 1, 2020

The main problem is that when a component is mounted, it starts a fetch, and because of the way suspense works, the component immediately gets unmounted. When the fetch is complete, the component will mount again, but because the default stale time is 0, it will trigger another fetch because the data is already considered stale. The double fetching is something we want to avoid.

Currently I can think of these solutions:

  1. Mention in the docs that when using suspense, it is recommended to also set a staleTime to prevent double fetches.
  2. Set some default stale time when suspense is enabled and staleTime is not set.
  3. Require users to assign some application wide unique ID to a query which can be used to track a specific query.

Besides this issue, if users also want to use suspend when refetching, maybe we should add a suspendOnRefetch option?

@tannerlinsley
Copy link
Collaborator

#1 sounds like the fastest way forward for now.
#2 is interesting, but finding a default that would work for everyone would be difficult given all of the async rendering happening with suspense.
#3 would definitely solve it, but that DX sounds pretty lame :(

In previous versions of RQ, we had some mutable properties on the query itself that would allow observers to detect if the last fetch was because of a suspension, and thus not trigger the fetch again on mount. I wonder if that same idea could work better now that the observers have become a bit smarter.

@Alexandredc
Copy link

Just found out this issue. I'm working on react-native project with react-query. Adding stale time doesn't trigger a new invokation of useQuery when a component is already suspended once :/

@nihgwu
Copy link

nihgwu commented Oct 17, 2020

@tannerlinsley any plan to fix this issue, I think this issue just block the use of suspense with react-query, as the query will never invalidate, that breaks the SWR(stale while revalidate) which is the biggest benefit of react-query

think about it, you have a query in one page, and then navigate to another page, the query should get invalidate and send a request again, but it doesn't when suspense enabled

@flybayer
Copy link
Contributor

Our current workaround in Blitz is to call invalidateQueries on page navigation events.

@tannerlinsley tannerlinsley added enhancement New feature or request help wanted Extra attention is needed needs-info suspense and removed bug Something isn't working labels Nov 3, 2020
@boschni
Copy link
Collaborator

boschni commented Dec 24, 2020

Is anyone still having trouble with suspense in v3?

@flybayer
Copy link
Contributor

I haven't had a chance to try v3 yet

@nihgwu
Copy link

nihgwu commented Dec 29, 2020

I tried it's fixed in V3

@nghiepdev
Copy link

@boschni
I had tried 3.11.0 but the suspense issue still again #1582

@rickyalmeidadev
Copy link

I faced the issue #1582 in version 3.17.0.

I built a simple demo in Code Sandbox where it is possible to reproduce it. Any plans to have it fixed?

@TkDodo
Copy link
Collaborator

TkDodo commented Jun 14, 2021

Is this the same as #2367?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request help wanted Extra attention is needed needs-info suspense
Projects
None yet
Development

No branches or pull requests

9 participants