You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Note: This is being posted as a Discussion because it describes an unreproducible bug that occurs intermittently in production environments.
Bug Report
There's a race condition between useQuery hooks and direct getQueryData() calls during cache invalidation, specifically when using polling mechanisms that frequently call invalidateQueries. Despite parent components using useQuery with conditional rendering that should prevent child component rendering when data is unavailable, child components using getQueryData() still throw "Right side of assignment cannot be destructured" errors.
Steps to reproduce
Context: Polling-based cache invalidation
Our app uses a polling mechanism that calls invalidateQueries every 3 seconds:
// Parent component with protective conditional renderingconstCartPage=()=>{const{ data }=useFetchCart(true);if(!data){return<EmptyCartTemplate/>;// Should prevent child rendering}return<GroupOrderCartTemplateContainer/>;// Only renders when data exists};// Child component using direct cache accessconstGroupOrderCartTemplateContainer=()=>{constcartQueryData=useGetCartQueryData<FetchCartResponse>();// Throws error!// ... rest of component};// Polling hook that triggers the issueconstuseSyncGroupOrderCartQuery=()=>{constqueryClient=useQueryClient();constcartQueryData=useGetCartQueryData<FetchCartResponse|null>();useFetchCartUpdateInfo(cartQueryData?.groupInfo?.groupCartVersion||'',{refetchInterval: 3_000,// Polls every 3 secondsmeta: {onSuccess: (data)=>{if(data?.status==='UPDATED'||data?.status==='EXPIRED'){// This invalidation causes the race conditionqueryClient.invalidateQueries({queryKey: cartQueryKeys.fetchCart().queryKey});}},},});};// useGetCartQueryData implementationexportconstuseGetCartQueryData=<T>()=>{constqueryClient=useQueryClient();constcartQueryData=queryClient.getQueryData<T>(cartQueryKeys.fetchCart().queryKey);if(cartQueryData===undefined){thrownewUndefinedCartDataError();// This throws despite parent having data}returncartQueryData;};
Reproduction steps:
Set up a parent component using useQuery with conditional rendering
Create a child component that uses getQueryData() directly
Implement a polling mechanism that calls invalidateQueries every 3 seconds
Navigate to the page and wait for polling to trigger invalidation
Observe intermittent errors where child component throws while parent still renders
Expected behavior
When a parent component using useQuery conditionally renders based on data availability, child components should never be rendered in a state where the same cached data is unavailable via getQueryData(). Both should access the same underlying cache consistently.
Your minimal, reproducible example
Note: This is an intermittent race condition that occurs in production environments but is difficult to reproduce consistently in isolated examples due to its timing-dependent nature.
Production Context:
High-traffic mobile web application serving millions of users
Error frequency: 11,000+ occurrences over 4 months (average 92 errors per day)
Affected users: 8,600+ unique users impacted
Error pattern: 99% occur in cart-related components using getQueryData()
The core issue remains: useQuery and getQueryData() can return inconsistent values for the same cache key during invalidation cycles, despite both supposedly accessing the same underlying cache.
Screenshots or Videos
Production error monitoring shows "TypeError: Right side of assignment cannot be destructured" occurring specifically in components using getQueryData(), while parent components using useQuery successfully render with data. The error consistently occurs at line positions where destructuring assignment is attempted on undefined values returned by getQueryData().
Platform
OS: iOS, Android
Browser: Mobile Safari, Chrome
Version: Primarily mobile browsers on recent Safari / Chrome Mobile WebView
TanStack Query information
TanStack Query version: v4.36.1
Adapter: React
TypeScript version: v5.0.4
Additional context
Production Error Analysis
Our error monitoring reveals consistent patterns:
User journey: Most errors occur during cart operations with active polling
Error persistence: Issue has been ongoing for 4+ months, indicating it's not an edge case
The Race Condition Pattern
The issue occurs when:
Polling API responds with status requiring cache invalidation
invalidateQueries called → cache marked as stale/invalidated
React Query starts refetch for the invalidated query
Parent component's useQuery still maintains previous data (React state)
Child component renders because parent condition passes
Is this expected behavior where useQuery and getQueryData() can return different values for the same cache key during invalidation? The documentation suggests they should access the same underlying cache, but our production data shows clear timing inconsistencies affecting thousands of users.
Current Workaround: Replace all getQueryData() calls with useQuery hooks, but this feels like it shouldn't be necessary given both should access the same cache.
Root Cause Hypothesis: The issue appears to be that useQuery maintains React state through QueryObserver subscriptions while getQueryData() directly accesses cache, creating a window where they're out of sync during invalidation + refetch cycles.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
-
Bug Report
There's a race condition between
useQuery
hooks and directgetQueryData()
calls during cache invalidation, specifically when using polling mechanisms that frequently callinvalidateQueries
. Despite parent components usinguseQuery
with conditional rendering that should prevent child component rendering when data is unavailable, child components usinggetQueryData()
still throw "Right side of assignment cannot be destructured" errors.Steps to reproduce
Context: Polling-based cache invalidation
Our app uses a polling mechanism that calls
invalidateQueries
every 3 seconds:Reproduction steps:
useQuery
with conditional renderinggetQueryData()
directlyinvalidateQueries
every 3 secondsExpected behavior
When a parent component using
useQuery
conditionally renders based on data availability, child components should never be rendered in a state where the same cached data is unavailable viagetQueryData()
. Both should access the same underlying cache consistently.Your minimal, reproducible example
Note: This is an intermittent race condition that occurs in production environments but is difficult to reproduce consistently in isolated examples due to its timing-dependent nature.
Production Context:
Stackblitz Demo: https://stackblitz.com/edit/tanstack-query-um5nzlel?file=src%2Findex.jsx
This demo shows the architectural pattern that causes the issue, but may not consistently reproduce the error due to the race condition's timing dependency.
Production Evidence:
getQueryData()
The core issue remains:
useQuery
andgetQueryData()
can return inconsistent values for the same cache key during invalidation cycles, despite both supposedly accessing the same underlying cache.Screenshots or Videos
Production error monitoring shows "TypeError: Right side of assignment cannot be destructured" occurring specifically in components using
getQueryData()
, while parent components usinguseQuery
successfully render with data. The error consistently occurs at line positions where destructuring assignment is attempted on undefined values returned bygetQueryData()
.Platform
TanStack Query information
Additional context
Production Error Analysis
Our error monitoring reveals consistent patterns:
The Race Condition Pattern
The issue occurs when:
invalidateQueries
called → cache marked as stale/invalidateduseQuery
still maintains previous data (React state)getQueryData()
returnsundefined
(cache already invalidated)Key Question
Is this expected behavior where
useQuery
andgetQueryData()
can return different values for the same cache key during invalidation? The documentation suggests they should access the same underlying cache, but our production data shows clear timing inconsistencies affecting thousands of users.Current Workaround: Replace all
getQueryData()
calls withuseQuery
hooks, but this feels like it shouldn't be necessary given both should access the same cache.Root Cause Hypothesis: The issue appears to be that
useQuery
maintains React state through QueryObserver subscriptions whilegetQueryData()
directly accesses cache, creating a window where they're out of sync during invalidation + refetch cycles.Beta Was this translation helpful? Give feedback.
All reactions