-
-
Notifications
You must be signed in to change notification settings - Fork 3.4k
[v4] Fix infinite re-renders with synchronous queries in suspense mode #9584
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
[v4] Fix infinite re-renders with synchronous queries in suspense mode #9584
Conversation
...nt infinite loops
Important
Review skipped
Auto reviews are disabled on base/target branches other than the default branch.
Please check the settings in the CodeRabbit UI or the .coderabbit.yaml
file in this repository. To trigger a single review, invoke the @coderabbitai review
command.
You can disable this status message by setting the reviews.review_status
to false
in the CodeRabbit configuration file.
✨ Finishing Touches
🧪 Generate unit tests
- Create PR with unit tests
- Post copyable unit tests in a comment
🪧 Tips
Chat
There are 3 ways to chat with CodeRabbit:
- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
I pushed a fix in commit <commit_id>, please review it.
Open a follow-up GitHub issue for this discussion.
- Files and specific lines of code (under the "Files changed" tab): Tag
@coderabbitai
in a new review comment at the desired location with your query. - PR comments: Tag
@coderabbitai
in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
Support
Need help? Create a ticket on our support page for assistance with any issues or questions.
CodeRabbit Commands (Invoked using PR/Issue comments)
Type @coderabbitai help
to get the list of available commands.
Other keywords and placeholders
- Add
@coderabbitai ignore
or@coderabbit ignore
anywhere in the PR description to prevent this PR from being reviewed. - Add
@coderabbitai summary
to generate the high-level summary at a specific location in the PR description. - Add
@coderabbitai
anywhere in the PR title to generate the title automatically.
CodeRabbit Configuration File (.coderabbit.yaml
)
- You can programmatically configure CodeRabbit by adding a
.coderabbit.yaml
file to the root of your repository. - Please see the configuration documentation for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation:
# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
Status, Documentation and Community
- Visit our Status Page to check the current availability of CodeRabbit.
- Visit our Documentation for detailed information on how to use CodeRabbit.
- Join our Discord Community to get help, request features, and share feedback.
- Follow us on X/Twitter for updates and announcements.
View your CI Pipeline Execution ↗ for commit 84f568a
☁️ Nx Cloud last updated this comment at |
This pull request is automatically built and testable in CodeSandbox. To see build info of the built libraries, click here or the icon next to each commit SHA. Latest deployment of this branch, based on commit 84f568a:
|
...nt infinite loops - fix test
...nt infinite loops - fix test
(削除) The v4 quarterly CI is currently delayed due to an issue (taking over an hour and failing due to an Nx Cloud error).
This issue appears to be similar in other v4 PRs (https://github.com/TanStack/query/actions/runs/16601605388/job/46962541804?pr=9334) as well as this PR. Local tests pass successfully.
Therefore, I will switch to Draft for now. (削除ここまで)
not sure why CI hangs here but I really don’t want to spend time backporting something to a 1+ year old code-base that has already been fixed in v5.
@manudeli if you want to investigate why this hangs, please do. Otherwise, let’s close this PR.
not sure why CI hangs here but I really don’t want to spend time backporting something to a 1+ year old code-base that has already been fixed in v5.
@manudeli if you want to investigate why this hangs, please do. Otherwise, let’s close this PR.
Totally understand not wanting to spend time backporting to the old v4 branch — thanks a lot for all the work you’ve already done there.
For now, @lachlancollins kindly offered in Discord to take a look at the CI issue, so I’ll wait for that. As for v4 backports, I think we’ll only need to cover a few essentials:
- infiniteQueryOptions, suspense hook
- Fix for infinite re-render in Suspense mode
- React 19 compatibility
Beyond these, I don’t expect further backports will be necessary. I’ll make sure to take care of these on my side, so you don’t need to worry about v4 anymore.
Hello @manudeli ,
I had removed the most critical test for this issue because I believed it was causing an Nx CI problem.
However, since it has been confirmed that this test does not cause CI issues, I will add it back.
it('should not cause infinite re-renders with synchronous query function and cacheTime: 0', async () => { const key = queryKey() let renderCount = 0 let queryFnCallCount = 0 const maxChecks = 20 function Page() { renderCount++ if (renderCount > maxChecks) { throw new Error(`Infinite loop detected! Renders: ${renderCount}`) } const result = useQuery( key, () => { queryFnCallCount++ return 42 }, { cacheTime: 0, suspense: true, }, ) return <div>data: {result.data}</div> } const rendered = renderWithClient( queryClient, <React.Suspense fallback="loading"> <Page /> </React.Suspense>, ) await waitFor(() => rendered.getByText('data: 42')) expect(renderCount).toBeLessThan(5) expect(queryFnCallCount).toBe(1) expect(rendered.queryByText('data: 42')).not.toBeNull() expect(rendered.queryByText('loading')).toBeNull() })
Uh oh!
There was an error while loading. Please reload this page.
fixes: #9583
comment: #9583 (comment)
Problem
After comparing the suspension-related codes, the two biggest differences between v5 and v4 are as follows.
In suspense.ts,
ensureStaleTime (renamed to ensureSuspenseTimers in v5) in v4 only includes a guard for staleTime, while in v5 it includes guards for both staleTime and gcTime.
query/packages/react-query/src/suspense.ts
Line 7 in 7e5e9f5
query/packages/react-query/src/suspense.ts
Line 21 in a1b1279
Another difference is that in v4, shouldSuspend determines suspension using willFetch(result, isRestoring), whereas in v5 it relies on result.isPending. However, since v4 does not handle isPending (more specifically, it does not handle thenables),
query/packages/react-query/src/suspense.ts
Line 30 in 7e5e9f5
query/packages/react-query/src/suspense.ts
Line 53 in a1b1279
I'm not sure if this is intentional, but in v5, when in suspense mode, ensureSuspenseTimers seems to set the minimum gcTime to 1000 (even if you explicitly set it to less than 1000).
Therefore, perhaps, the simplest solution without major structural changes is to add a guard for cacheTime to ensureStaleTime.
Solution
This PR adds a guard for cacheTime in the ensureStaleTime function to match v5's behavior. When suspense mode is enabled, the function now ensures a minimum cacheTime of 1000ms, preventing the cache from being deleted before React's rendering cycle completes.
The fix specifically addresses the scenario where synchronous query functions with suspense: true and cacheTime < 2ms cause infinite re-render loops. By enforcing a minimum cacheTime, we ensure that
Testing
Added comprehensive test cases to verify