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

useQuery randomly not returning data and stuck in loading #1657

Closed
vsaarinen opened this issue Jan 15, 2021 · 89 comments · Fixed by #1737
Closed

useQuery randomly not returning data and stuck in loading #1657

vsaarinen opened this issue Jan 15, 2021 · 89 comments · Fixed by #1737

Comments

@vsaarinen
Copy link

Describe the bug
After upgrading to react-query 3.5.11 from 2.26.4 last week, our project's Cypress tests started failing randomly because the app started showing a loading spinner at random times. After a lot of head-banging, we finally tracked down the issue: even though, based on the network inspector, the request had gone through successfully, react-query's useQuery was returning an object with isLoading as true and data as undefined. Reverting back to react-query 2 fixed the problems.

Most of the requests in our Cypress tests are stubbed using cy.server and cy.route. This probably shouldn't affect react-query but mentioning it here just in case.

Our cache is configured in the following manner at the top of one of our source files, so this shouldn't be caused by us accidentally creating multiple caches:

const queryCache = new QueryCache({
  defaultConfig: {
    queries: {
      staleTime: 10 * 60 * 1000,
      cacheTime: 15 * 60 * 1000,
      retry: (
        _failureCount,
        error: any,
      ) => error.status !== 403 && error.status !== 401,
    },
  },
});

// Further down:

class AppProvider extends Component<{}, State> {
  public render() {
    return (
      <ReactQueryCacheProvider queryCache={queryCache}>
        {/* ... */}
      </ReactQueryCacheProvider>
    );
  }
}

To Reproduce
Unfortunately, this happens randomly and is thus very hard to reproduce. The randomness does point towards some type of race condition. Our app performs many (~10) requests on startup.

Even though which tests fail is random, I can consistently get at least one test to fail. Is there something I can do to help track down what could be the issue?

Expected behavior
useQuery should return the data it receives in the response once the request is successful.

Desktop (please complete the following information):

  • OS: MacOS
  • Browser: Chrome
  • Version: 87

Additional context
Happens on both Cypress version 5.3.0 and 6.2.1.

@TkDodo
Copy link
Collaborator

TkDodo commented Jan 15, 2021

this looks surprisingly similar to #1653

@boschni
Copy link
Collaborator

boschni commented Jan 16, 2021

Don't know how the tests are written but it might be good to check if they do not implicitly depend on certain timings. Are you able to reproduce it manually? This might be useful: https://docs.cypress.io/guides/core-concepts/retry-ability.html

@vsaarinen
Copy link
Author

Don't know how the tests are written but it might be good to check if they do not implicitly depend on certain timings. Are you able to reproduce it manually? This might be useful: https://docs.cypress.io/guides/core-concepts/retry-ability.html

I haven't noticed this problem when using the app manually, so there's always a chance that this is somehow caused by Cypress. But a few reasons why I think this may be an issue with react-query:

  • The error (loading spinner) occurs on app startup, before any interaction from Cypress. So it shouldn't be due to timing of commands/assertions.
  • The problem goes away when react-query is downgraded to an old version.
  • When inspecting the app, the network requests show that the response was received by the app but the related useQuery call returns undefined for data.

I'll see if I can narrow down the react-query version where this started happening.

@antonversal
Copy link

I can confirm the same behavior after migrating from 2.23.1 to 3.5.16.
isLoading is randomly returning true for queries with staleTime option. Everything works without staleTime.

@boschni
Copy link
Collaborator

boschni commented Jan 22, 2021

Would be great if you guys could to provide a sandbox or something similar which we can use to debug the issue.

@antonversal
Copy link

Would be great if you guys could to provide a sandbox or something similar which we can use to debug the issue.

I've tried to reproduce in a sandbox without any luck (

@josewhitetower
Copy link

Commenting just to subscribe to the thread because I'm having the same issue and can't reproduce it on a sandbox. Happens on v2.26.3 and after updating to v3.6.0.

@TkDodo
Copy link
Collaborator

TkDodo commented Jan 24, 2021

Can we try to narrow it down collectively? So far, we've seen that:

could you further narrow it down to a specific version between those two?

staleTime seems to be involved, but how do you set staleTime? via the default-config on the global cache like @vsaarinen has shown in the opening post (similar to what @fTrestour has posted in #1653), or are you setting the staleTime on the useQuery call directly?

@TkDodo
Copy link
Collaborator

TkDodo commented Jan 24, 2021

I'm also seeing that both the opening post in this issue as well as in #1653 have the retry option set - could we rule that one out as well?

@antonversal
Copy link

@TkDodo I set staleTime in useQuery. No global settings and retry.

@vsaarinen
Copy link
Author

Can we try to narrow it down collectively? So far, we've seen that:

could you further narrow it down to a specific version between those two?

For me, things work fine on 2.26.4. I'll try to find which version of 3 causes things to start breaking again.

@vsaarinen
Copy link
Author

vsaarinen commented Jan 25, 2021

Started seeing the same issues after upgrading to 3.2.0 from 2.26.4. So it appears to be a regression introduced in version 3.

@adeelibr
Copy link

Started seeing the same issues after upgrading to 3.2.0 from 2.26.4. So it appears to be a regression introduced in version 3.

I have the same issue

@fermmm
Copy link

fermmm commented Jan 28, 2021

I finally found the cause at least for my specific case.

React-query performs a deep compare to update the component, so if your request returns the same data than in the previous request, react query will not update your component. This was happening randomly to me because the response of my request contains random changes but sometimes returns the same data. In my case I expect a re-render always.
The solution in case you have the same problem is to disable deep compare by setting structuralSharing to false like this:

export const queryClient = new QueryClient({
   defaultOptions: {
      queries: {
         staleTime: Infinity,
         structuralSharing: false
      }
   }
});

This is a bug or at least confusing because it's an "anti-pattern" since react does not expose anything that works with deep compare like this. React is based on reference equality only.

This feature was introduced recently:
#430

@vsaarinen
Copy link
Author

@fermmm It sounds like your issue may be different than what I'm experiencing. My issue is that even though data was successfully received by the app, useQuery thinks that it's still loading the data and never provides it to the app.

@fermmm
Copy link

fermmm commented Jan 29, 2021

@fermmm It sounds like your issue may be different than what I'm experiencing. My issue is that even though data was successfully received by the app, useQuery thinks that it's still loading the data and never provides it to the app.

Yes my problem looks similar but may be not the same.

@fermmm
Copy link

fermmm commented Feb 1, 2021

Ok I had this issue in another request of my app and this time was not related with structuralSharing, I noticed {data: undefined, isLoading: true} randomly without getting the data. The only different thing with this useQuery() it was that I had many components mounted using the same useQuery() key, maybe is something that happens when one component is waiting for the request and other mounts with the same... I don't know.

@vasconita
Copy link

vasconita commented Feb 1, 2021

Same problem here. In my case I use staleTime: Infinity.

@TkDodo
Copy link
Collaborator

TkDodo commented Feb 1, 2021

I would really like to get to the bottom of this issue, but if nobody can reproduce it in a codesandbox, it is almost impossible to narrow down.

@vasconita
Copy link

I forgot to add more info: I'm mounting a lot of components (50) that use this useQuery with Infinity staleTime and I don't have configured retry neither. I'm going to try to reproduce it in a sandbox.

@vasconita
Copy link

I have created a sandbox with the conditions that seem to be common to all of us (Infinity staleTime and having many instances) but cannot reproduce the error, I think it has only happened to me once. On my SPA it happens to me much more easily 🤔

@TkDodo
Copy link
Collaborator

TkDodo commented Feb 1, 2021

From what I can see in the sandbox: are you also putting the whole result from fetch into the query cache in your app? This includes some non-serializable data, like the body, which is a stream, and lots of functions that return Promises, like .json()...

@vasconita
Copy link

No no, it was like this because since I don't use data in the example I didn't even worry with the response. I updated it and now I just return the JSON body (like in my app of course)

@fermmm
Copy link

fermmm commented Feb 1, 2021

In my case this only happens when I restart my RN app when all the components are loading for the first time

@vsaarinen
Copy link
Author

I believe my issue has also appeared only on app start, in the initial loading of components.

@TkDodo
Copy link
Collaborator

TkDodo commented Feb 1, 2021

I believe my issue has also appeared only on app start, in the initial loading of components.

I guess this is the only time when you can be "stuck in loading", because you are never in loading state again once you've successfully loaded data.

Question: In case this being-stuck happens, what do the react-query devtools say about the state of the query?

@vsaarinen
Copy link
Author

vsaarinen commented Feb 2, 2021

Question: In case this being-stuck happens, what do the react-query devtools say about the state of the query?

The problematic query is the second from the bottom in the list, with 5 listeners and marked as stale:
Screenshot 2021-02-02 at 8 54 37
Screenshot 2021-02-02 at 8 54 47

I confirmed that the data for that query was received successfully when looking at the network developer tab. The weird thing is that the data is shown correctly in the devtools, but in our app the data property of the objected returned by that useQuery is undefined. Is there something more specific I should share from the devtools?

@TkDodo
Copy link
Collaborator

TkDodo commented Feb 2, 2021

The weird thing is that the data is shown correctly in the devtools, but in our app the data property of the objected returned by that useQuery is undefined.

Yes, that does indeed seem weird. Are you using different staleTimes on the 5 observers? Everyone is mentioning staleTime: Infinity, but in your code, I am seeing a different staleTime - and your query is also marked as stale, which shouldn't be the case with staleTime: Infinity...

@vsaarinen
Copy link
Author

vsaarinen commented Feb 2, 2021

Yes, that does indeed seem weird. Are you using different staleTimes on the 5 observers? Everyone is mentioning staleTime: Infinity, but in your code, I am seeing a different staleTime - and your query is also marked as stale, which shouldn't be the case with staleTime: Infinity...

I've never used staleTime: Infinity. Except for a couple of queries where these are overridden, I only use the time values from the defaultConfig as shown in the original post:

      staleTime: 10 * 60 * 1000,
      cacheTime: 15 * 60 * 1000,

Could the issue be somehow related to the fact that the devtools marks the query as stale even though it shouldn't be? Just guessing here since I don't know what "stale" means...

@balteo
Copy link

balteo commented Jun 14, 2022

@TkDodo Thanks a lot for your detailed reply!

@Stryzhevskyi
Copy link

Hi
I have a similar issue on the version 4. I see there is a tag port-to-v4 in the #1737 fix, but was this fix really ported to v4? I'm not familiar with the organisation of the repo. There are 4 branches: 1.x, 2.x, v3, v5 and none for v4.

@TkDodo, I'm sorry for disturbing you, but could you check if this fix is present in v4, please?

Screen.Recording.2023-02-01.at.16.01.17.mov

I've spent many hours trying to track the root cause o the issue, because it happens only on CI (like 1/5 times) on my project and then found this issue for v3.

@TkDodo
Copy link
Collaborator

TkDodo commented Feb 1, 2023

I renamed the port-to-v3 label as port-to-v4, that was probably not a good idea, sorry.

code is quite different now, I would need to see a codesandbox reproduction that fails on v4 please.

the code path you're showing has been removed in a later v3 version to ensure concurrent rendering capabilities:

@annjawn
Copy link

annjawn commented Feb 15, 2023

I am going to add a +1 to this. I am facing this exact issue where isLoading gets stuck in true and data comes back as undefined. I am using the same query in two components the first component fires the query and gets the data fine, but on the second component the same query (with the same key) gets stuck and returns undefined. This is a bizarre situation that I am not sure I am able to wrap my head around.

Here's whats happening

Component - 1
    Query 1 --> Works 
    Query 2 --> Works

Component - 2
   Query 1 --> Works with same key as in component 1
   Query 2 --> Doesn't Work with same or different key

Component 1 and 2 are quite similar in how they work, Query-1 and Query-2 uses the same async functions (with different service endpoints). I will say that at some point I had this QueryClient setup (I am not sure why I setup this way)

const queryClient = new QueryClient({defaultOptions: {
  queries: {
    retryDelay: 1,
    retry:0,
  }
}});

Changing this to the following didn't work either

const queryClient = new QueryClient();

@annjawn
Copy link

annjawn commented Feb 15, 2023

ok sheesh i think i found the issue. With Multiple queries comes multiple isLoading the part of the component that depended on the second query was rendering too soon causing the issue. So these multiple isLoadings and checking of data for each of these queries will need to be handled individually. Any early rendering causes the query status to stay stuck (or rather give the illusion i should say).

@filip-kis-fsc
Copy link

I am experiencing this issue consistently in a larger project with

    "@tanstack/react-query": "4.26.0",
    "react": "18.2.0",
    "next": "13.2.3",

but only if react is running in strict mode.

Unfortunately I do not have a minimal repro example.

@hectopod
Copy link

hectopod commented May 8, 2023

I am facing the same issue only in Cypress. (Edit: working in Playwright, switching to it)

Devtools indicate that the data is fresh but the loader is still loading (isFetching).

I am using react query for server state and redux for client state. Could be related

@agos
Copy link

agos commented May 9, 2023

I'm also facing the same issue since recently on v4.

invalidating a query (as result of a mutation, or manually via dev tools) will make it transition to 'isFetching', but won't transition again to 'success'. DevTools show it stuck in fetching. Components using the query will render only once and see it in 'success' status with old data, never receiving the updated data.

Unfortunately no minimal repro from me either, but I have noticed that this happened once I moved some of the query dependencies out of a context and inside a state manager (Jotai). In theory this does not imply any change in the query rendering, but the timing is very suspicious in my case

@crutchcorn
Copy link
Member

One thing to remember if you're in the MSW/Testing land - set retry to 0 during testing.

Remember that Query intentionally backs off over a period of time to retry searching, upwards of a couple of seconds. This is a good thing, as it leads to better UX.

As a result, my tests were failing until I did this.

@vaibhavshn
Copy link

vaibhavshn commented Sep 29, 2023

We also faced the same issue where for some reason some users were not able to fetch the data properly.

On further debugging found the fetchStatus was always paused. After going through docs, we passed the networkMode: always and it worked fine this time. We also verified this by removing the networkMode config.

So this probably means something wrong is happening in the code when the default networkMode: on value is set.

cc @TkDodo

NOTE: Using the latest version current: 4.35.3

@TkDodo
Copy link
Collaborator

TkDodo commented Sep 29, 2023

So this probably means something wrong is happening in the code

no, this is an issue with navigator.onLine in chrome on macos, which we are using in v4 to determine if you are online or not.

Since navigator.onLine is broken, we've moved away from using it in v5

@vaibhavshn
Copy link

Since navigator.onLine is broken, we've moved away from using it in v5

Thanks for the quick reply, will upgrade to v5.

@Dougmarcolino
Copy link

Dougmarcolino commented Oct 12, 2023

I just needed to update to the new version of Chrome, now it's working.

image

macOS: Ventura 13.5.2 (Apple M1 Pro)

@Harvel-Kay
Copy link

See am encountering the same issue with v4.36,
It tends to make requests successfully to jsonplaceholder however when I make a request to my local server it then stucks on the loading,
When I use the use Effect hook and state everything works well, I've seen this stuck issue has been perturbing from v2 all the way to v4, why on earth haven't you figured out why your library behaves like this, it's pretty frustrating and I'll chose to revert to the older way of fetching data 😡😡😡😡😠😠

@hoanmq
Copy link

hoanmq commented Oct 20, 2023

We have a large number of users using the app, occasionally a few users encounter this issue where all the functions using react query get stuck at the loading stage, whereas direct calls using axios to the API work fine. The characteristic is that the loading is shown, but no requests seem to be made to the server. When we instruct the users to completely close the browser and reopen it, everything starts working normally again. It's quite strange.

  "@tanstack/react-query": "^4.24.4"
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      refetchOnWindowFocus: false,
    },
  },
});

@TkDodo
Copy link
Collaborator

TkDodo commented Oct 20, 2023

It's quite strange.

again, I'm positive that this is related to navigator.onLine being broken in chrome, which is not our fault. What is our fault is that we were relying on it to work properly in the first place, which is what we've changed in v5.

you can validate that assumption by looking at the fetchStatus returned from useQuery when that happens. If the fetchStatus is 'paused', you'll know that this is indeed the case.

again, you can:

  • update to v5, which has a different default behaviour
  • set networkMode: 'offlineFirst' to work around it
  • get the bugs fixed in chrome 🤷

@hoanmq
Copy link

hoanmq commented Oct 20, 2023

It's quite strange.

again, I'm positive that this is related to navigator.onLine being broken in chrome, which is not our fault. What is our fault is that we were relying on it to work properly in the first place, which is what we've changed in v5.

you can validate that assumption by looking at the fetchStatus returned from useQuery when that happens. If the fetchStatus is 'paused', you'll know that this is indeed the case.

again, you can:

  • update to v5, which has a different default behaviour
  • set networkMode: 'offlineFirst' to work around it
  • get the bugs fixed in chrome 🤷

Thank you for responding, however, it happens randomly across different browsers including Edge, Brave, etc.
I will try upgrading to v5.

@TkDodo
Copy link
Collaborator

TkDodo commented Oct 20, 2023

edge and brave are both built on chromium ... they are the same thing and have the same behaviour / bugs in this regard

@117
Copy link

117 commented Dec 9, 2023

Firefox 120.0.1 (64-bit) still occurring M2 Mac, Sonoma 14.1.1

@TkDodo
Copy link
Collaborator

TkDodo commented Dec 9, 2023

@117 on query v4 or v5? We haven't done anything on v4 - and also won't because the fix / workaround was a change in behaviour, so it's a breaking change

@lundstromdavid
Copy link

I've had the same problem for a while and haven't bothered to investigate until now. I've now figured out the reason which for me was that I called queryClient.reset() on mount, as a side effect of resetting the client when the user signs out.

Here's a sandbox: https://codesandbox.io/p/sandbox/react-query-bug-sfm3fx?file=%2Fsrc%2FApp.tsx%3A16%2C2

I've also included the fix that worked for me, which you can toggle on/off using the useFix flag in App.tsx.

Is this a bug in React Query or just bad implementation on my part? @TkDodo

@TkDodo
Copy link
Collaborator

TkDodo commented Jan 3, 2024

@lundstromdavid it seems like you're clearing the cache while the query is in-flight, I don't know why you would do something like that. clear() is not the same as resetQueries() because clear does not inform observers about the change

@lundstromdavid
Copy link

lundstromdavid commented Jan 3, 2024

@TkDodo Thanks for the reply! Should have read up more before. Although it does seem quite unintuitive for me that clear() can stall in-flight queries in a loading state, but what do I know 🤷‍♂️

@shuweili1122
Copy link

shuweili1122 commented Jan 26, 2024

can confirm this indeed happens with react: 18.2, and @tanstack/react-query 4.36.1 on post request when running jest test with axios 1.6.2 and axios-mock-adapter 1.22.0, still happens after upgrading @tanstack/react-query to 5.17.19, I can see the result in useQuery, but it never returns to the component, and the status is always loading; hence replaced @tanstack/react-query with vanilla axios for my data fetching part, and the failed test passes...

Also found a very hacky workaround if you don't want to replace @tanstack/react-query with other libraries: basically just do a data fetching with axios-mock-adapter in beforeAll or beforeEach, then run your regular test, and in your test block, do a data fetching with axios-mock-adapter again... your test will then have about 1/2 chance of passing... super confusing bug

@TkDodo
Copy link
Collaborator

TkDodo commented Jan 26, 2024

One thing you can try on v5 is to disable our batching and see if that helps:

import { notifyManager } from '@tanstack/react-query'

// invoke callback instantly
notifyManager.setScheduler((cb) => cb())

see: https://tanstack.com/query/latest/docs/reference/notifyManager#notifymanagersetscheduler

@Codyooo
Copy link

Codyooo commented May 8, 2024

I would like to recommend changing the default setting of the networkMode configuration in React Query to 'always'. Given the library's widespread use, with over 2 million downloads per week, the current default setting may lead to significant issues.

Recently, we encountered a bug where the system did not make any network requests due to the networkMode being set to 'online'. This issue arose because the browser's navigator.onLine API inaccurately reported the network status as offline, even though the connection was active. The impact of this bug is particularly severe as it can cause key pages to remain stuck on a loading screen without any indication of the problem. Considering that React Query is a critical part of many projects, such errors can have drastic consequences, including potential job losses, especially if key stakeholders encounter these issues firsthand.

It can be challenging to identify this issue without delving into the source code, making it a silent but deadly problem. To enhance reliability and prevent such problematic scenarios, I strongly urge you to consider setting the default networkMode to 'always'. This change would ensure that network requests are made regardless of the navigator.onLine status, thus providing a more consistent and dependable user experience.

Thank you for considering this suggestion. I believe it will significantly improve the robustness of React Query and safeguard many developers and businesses from facing similar issues.

@manvydas-tbol
Copy link

One thing you can try on v5 is to disable our batching and see if that helps:

import { notifyManager } from '@tanstack/react-query'

// invoke callback instantly
notifyManager.setScheduler((cb) => cb())

see: https://tanstack.com/query/latest/docs/reference/notifyManager#notifymanagersetscheduler

Hey @TkDodo
I was facing identical issue. This helped when running cypress tests. Is there any other way to achieve this without changing global configuration?

@TkDodo
Copy link
Collaborator

TkDodo commented Jun 7, 2024

you can set the scheduler inside cypress tests only.

@manvydas-tbol
Copy link

Hey @TkDodo
Thank for quick reply.

However, adding it like this

it('Cypress test demo', () => {
   notifyManager.setScheduler((cb) => cb());

   // Test logic
});

or this

notifyManager.setScheduler((cb) => cb());
...

    it('Cypress test demo', () => {
 
       // Test logic
    });

Doesn't work. Only works if directly added to the component

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.