<!--
title: Stable, lifecycle-correct hooks
domain: app-frameworks
topic: React
language: TypeScript
source: tanstack/query
updated: 2026-04-10
url: https://awesomereviewers.com/reviewers/query-stable-lifecycle-correct-hooks/
-->

Ensure React hooks and query integrations remain correct across renders, SSR/hydration, and subscriptions.

Rules:
1) Keep QueryClient stable in React components
- Create it once using one of: `React.useState(() => new QueryClient())`, `React.useMemo(() => new QueryClient(), [])`, or a lazy `useRef` initializer.
- Avoid `new QueryClient()` directly in component render.
- In Server Components, creating a new `QueryClient` per request is fine.

2) Don’t break subscription/snapshot invariants
- Avoid reading refs (or mutating derived values) during render for SyncExternalStore/getSnapshot-style logic.
- If you need “latest filters/options”, update a ref in `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
- Make the initial/optimistic state that React renders match the server render.
- If stale data requires a refetch, trigger it after the first render/mount to prevent hydration mismatches.

4) Use supported disabling patterns
- If you want to disable conditionally, prefer `skipToken` over returning/defining a queryFn that will error while `enabled: false` patterns would otherwise spam logs.

Example (stable QueryClient + latest filters ref pattern):
```tsx
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:
```tsx
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.
```
