ngneat / query

🚀 Powerful asynchronous state management, server-state utilities and data fetching for Angular Applications

Home Page:https://ngneat.github.io/query

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Query invalidation not working in some cases

dknogl opened this issue · comments

Which @ngneat/query-* package(s) are the source of the bug?

query

Describe the bug

When creating and subscribing to the same query result multiple times (same key and query function) and unsubscribing one of the results (e.g. in a modal/dialog that gets destroyed after a while), it will cause a bug that prevents you from invalidating the query. If you try to invalidate (e.g. using invalidateQueries) the query, it will get stuck in "fetching" status forever.

Steps to reproduce

  • Create a query twice with same key and query function
  • Subscribe to both query results (Observer count in dev tools changes to 2)
  • Unsubscribe to second query (Observer count in dev tools changes to 1)
  • Try to invalidate query using query key via invalidateQueries
  • Dev Tools will show you that query is in 'fetching' state forever and will not succeed

Cause

I guess this is happening because of line 76 in base-query.ts:
(mergedOptions as any)['queryFn']?.[SUBSCRIPTION]?.unsubscribe();

Because the second query result gets unsubscribed it will unsubscribe sourceSubscription (utils.ts). Next time you try to invalidate, it will use the query function (queryFn$) to create new subscription and assigning it to the unsubscribed sourceSubscription. This will cause the originalQueryFn function to immediately get unsubscribed and the Promise that is returned will never resolve. Therefore the query is stuck forever.

Possible Solution

Maybe it would be enough to recreate the sourceSubscription every time the query function is executed.

Please provide a link to a minimal reproduction of the bug

No response

Please provide the exception or error you saw

No response

Please provide the environment you discovered this bug in

No response

Anything else?

No response

Do you want to create a pull request?

No

Can you add a basic code example, please?

https://codesandbox.io/s/amazing-sky-vz432k

Just prepared a simple example. You can test it by simply pressing "Toggle Dialog" twice and then pressing "Invalidate Query". When opening the dev tools you will see that the query is now stuck in "fetching" status and the request will never resolve.

Just a little more context: "Toggle Dialog" will mount the dialog component that is using the same query. After pressing "Toggle Dialog" again, the dialog component gets destroyed and therefore the query gets unsubscribed. Now when you try to invalidate the still active query from app.component its not working anymore. Hope this helps.

I checked the example in the playground, and it seems to work fine. You can see in Chrome network that it's re-fetched when you click invalidate. Sometimes, the devtools are stuck in fetching, but the library itself works as expected.

https://codesandbox.io/s/goofy-silence-odrljy

I just altered my example from above to use some random data, this way its more clear what happens. You are correct, the request is actually done but the problem is the subscription, because it is immediately closed it will never resolve the promise that is returned to tanstack query, even though the request is made and actually returning something.

You will see in my example that every time you press invalidate new data will appear in the list. When you toggle the dialog it will also refresh the data, but once you close the dialog again and try to invalidate it will stop refreshing the data, this is because the query is now stuck. The dev tools are correct here, the promise that is used by the query function will never resolve because the next() function of the subscription is never called and therefore it is in "fetching" status.

Please have a look at line 34-35 in query/utils.ts, this is never called in my example because sourceSubscription in line 19 is already unsubscribed. Therefore the promise res function is never called and thats why the promise will not resolve.

Interesting. The second call works.

I see what's the issue. You're using the same key; therefore, the query already uses the same function you pass at the beginning. We'll need to check what's the behavior in react-query when using the same key with different queryFn.

Can you retry with latest version, please?

It seems that with the newest or a previous version this was fixed codesandbox.
Its not stuck anymore on fetching when trying to reproduce the steps mentioned in this issue.