-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
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
Infinite Query runs twice on initial load with React 18 #3492
Comments
interesting, thanks for reporting.
Nevertheless, the requests should be deduped, even with strict effects, so we'll have to dig into it. |
I found the issue: we are wrongfully thinking that the provided It is a somewhat known issue that with strict effects in strict mode + query cancellation, you will get the first request aborted, because what strict effects do is:
this is not problematic without query cancellation, because the second request will just be de-duplicated by RQ. However, with cancellation, this is the "correct" behavior, because we need to abort when the last observer unsubscribes. Here is the relevant discussion in the react 18 working group if you're interested: I will document this behaviour on the query cancellation page for v4. The good news is that this only shows up in development mode, so it is nothing that affects production, and the workaround is to disable strict mode. however, the the real bug is that we are consuming the abort signal internally, even though the user (=you) hasn't consumed it. This effectively means that every infinite query is treated as aborted, so if you unmount, the data that comes in later won't be put into the cache. I need to think a bit about a solution to this. pulling in @prateek3255 as you contributed that change with refactor: Remove deprecated promise cancel #2996 I have to say I don't fully understand how the AbortSignal works right now for Infinite Queries. What is also a bit weird is that the infinite query behaviour overwrites the we do have a slight duplication inside the infinite query behaviour, but it has diverged from the original one: So I think in any case, we should pass the "original queryFn" to the infinite query behaviour to fix that. And as a second thing, find out if we can also streamline the abortSignal behaviour. I would truly appreciate everyone's help on this. Thank you 🙏 |
I can't say for sure this is the correct behavior for infinite query, but it should avoid consuming the signal internally (at least until the user actually consumes it). diff --git a/src/core/infiniteQueryBehavior.ts b/src/core/infiniteQueryBehavior.ts
index 3fe93a0..ebe073c 100644
--- a/src/core/infiniteQueryBehavior.ts
+++ b/src/core/infiniteQueryBehavior.ts
@@ -6,7 +6,6 @@ import type {
QueryOptions,
RefetchQueryFilters,
} from './types'
-import { getAbortController } from './utils'
export function infiniteQueryBehavior<
TQueryFnData,
@@ -24,11 +23,25 @@ export function infiniteQueryBehavior<
const isFetchingPreviousPage = fetchMore?.direction === 'backward'
const oldPages = context.state.data?.pages || []
const oldPageParams = context.state.data?.pageParams || []
- const abortController = getAbortController()
- const abortSignal = abortController?.signal
let newPageParams = oldPageParams
let cancelled = false
+ const addSignalProperty = (object: unknown) => {
+ Object.defineProperty(object, 'signal', {
+ enumerable: true,
+ get: () => {
+ if (context.signal?.aborted) {
+ cancelled = true;
+ } else {
+ context.signal?.addEventListener('abort', () => {
+ cancelled = true
+ })
+ }
+ return context.signal;
+ },
+ })
+ }
+
// Get query function
const queryFn =
context.options.queryFn || (() => Promise.reject('Missing queryFn'))
@@ -62,11 +75,12 @@ export function infiniteQueryBehavior<
const queryFnContext: QueryFunctionContext = {
queryKey: context.queryKey,
- signal: abortSignal,
pageParam: param,
meta: context.meta,
}
+ addSignalProperty(queryFnContext);
+
const queryFnResult = queryFn(queryFnContext)
const promise = Promise.resolve(queryFnResult).then(page =>
@@ -143,11 +157,6 @@ export function infiniteQueryBehavior<
pageParams: newPageParams,
}))
- context.signal?.addEventListener('abort', () => {
- cancelled = true
- abortController?.abort()
- })
-
return finalPromise
}
}, |
🎉 This issue has been resolved in version 4.0.0-beta.16 🎉 The release is available on: Your semantic-release bot 📦🚀 |
So I'm seeing a double fetch in strict mode while using See https://codesandbox.io/s/crazy-khayyam-9jb1wy?file=/src/App.js Perhaps this is now just expected behavior to see the cancellation in the network tab? |
Yes it is, that's how cancellation works. First request is aborted, second request goes through. Just as if the user were to navigate away from and then back to your page very fast. If you don't want that, don't use cancellation :) then, the second fetch will just pick up the already running one. |
Describe the bug
I'm currently using the latest Beta version of React Query with React 18 and noticed my Infinite Query to fetch the initial page two times. As soon as I switch to the React 17 DOM setup, everything works fine.
I added a minimal reproduction example.
Your minimal, reproducible example
https://codesandbox.io/s/restless-firefly-zq2cmo
Steps to reproduce
Open the network tab, refresh the Browser output and see two API calls.
Expected behavior
Just one time would be great.
How often does this bug happen?
No response
Screenshots or Videos
No response
Platform
...
react-query version
v4.0.0-beta.1
TypeScript version
No response
Additional context
No response
The text was updated successfully, but these errors were encountered: