Ensure React hooks and query integrations remain correct across renders, SSR/hydration, and subscriptions.
Rules: 1) Keep QueryClient stable in React components
React.useState(() => new QueryClient()), React.useMemo(() => new QueryClient(), []), or a lazy useRef initializer.new QueryClient() directly in component render.QueryClient per request is fine.2) Don’t break subscription/snapshot invariants
useEffect, and read the real source of truth inside the correct React lifecycle (e.g., getSnapshot) rather than during render.3) Hydration-safe first render
4) Use supported disabling patterns
skipToken over returning/defining a queryFn that will error while enabled: false patterns would otherwise spam logs.Example (stable QueryClient + latest filters ref pattern):
import React from 'react'
import { QueryClient } from '@tanstack/react-query'
export function App() {
const [queryClient] = React.useState(() => new QueryClient())
// ...useQuery hooks using queryClient
return null
}
And for “latest value” without render-time ref reads:
const [state, setState] = React.useState(() => client.isFetching(initialFilters))
const filtersRef = React.useRef(filters)
React.useEffect(() => {
filtersRef.current = filters
}, [filters])
// Ensure the snapshot/getter reads in the right lifecycle, not during render-time ref reads.